Skip to content

Commit

Permalink
Add scaffolding command for c-sharp
Browse files Browse the repository at this point in the history
  • Loading branch information
markcowl committed Jan 24, 2025
1 parent 9517d45 commit 5c7da4f
Show file tree
Hide file tree
Showing 22 changed files with 3,214 additions and 1,795 deletions.
8 changes: 8 additions & 0 deletions .chronus/changes/scaf-01-2025-0-17-0-26-24.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
changeKind: fix
packages:
- "@typespec/http-server-csharp"
---

Add scaffolding option for csharp generator
7 changes: 6 additions & 1 deletion cspell.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ words:
- espt
- ESRP
- etree
- fastify
- fluentui
- français
- genproto
- getpgid
- giacamo
Expand All @@ -80,8 +82,11 @@ words:
- Hdvcmxk
- headasbooleanfalse
- headasbooleantrue
- hscs
- HSTS
- imple
- Infima
- initcs
- inlines
- inmemory
- instanceid
Expand Down Expand Up @@ -203,6 +208,7 @@ words:
- ssvs
- statment
- strs
- swaggerui
- syncpack
- TCGC
- timegm
Expand Down Expand Up @@ -253,7 +259,6 @@ words:
- xplat
- xxsubtype
- yamls
- français
ignorePaths:
- "**/node_modules/**"
- "**/dist/**"
Expand Down
2 changes: 2 additions & 0 deletions packages/http-server-csharp/cmd/hscs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env node
import "../dist/src/cli/cli.js";
23 changes: 16 additions & 7 deletions packages/http-server-csharp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,18 @@
"c-sharp"
],
"type": "module",
"main": "dist/src/index.js",
"bin": {
"hscs": "./cmd/hscs.js"
},
"main": "dist/src/lib/index.js",
"exports": {
".": {
"types": "./dist/src/index.d.ts",
"default": "./dist/src/index.js"
"types": "./dist/src/lib/index.d.ts",
"default": "./dist/src/lib/index.js"
},
"./testing": {
"types": "./dist/src/testing/index.d.ts",
"default": "./dist/src/testing/index.js"
"types": "./dist/src/lib/testing/index.d.ts",
"default": "./dist/src/lib/testing/index.js"
}
},
"engines": {
Expand All @@ -46,7 +49,7 @@
"regen-docs": "echo No doc generation for alpha version"
},
"files": [
"lib/*.tsp",
"lib/**/*.tsp",
"dist/**",
"!dist/test/**"
],
Expand All @@ -57,16 +60,22 @@
"@typespec/versioning": "workspace:~"
},
"dependencies": {
"change-case": "~5.4.4"
"change-case": "~5.4.4",
"cross-spawn": "^7.0.6",
"picocolors": "~1.1.1",
"yargs": "~17.7.2"
},
"devDependencies": {
"@types/node": "~22.10.7",
"@types/yargs": "~17.0.33",
"@types/cross-spawn": "~6.0.6",
"@typespec/compiler": "workspace:~",
"@typespec/http": "workspace:~",
"@typespec/library-linter": "workspace:~",
"@typespec/openapi": "workspace:~",
"@typespec/rest": "workspace:~",
"@typespec/tspd": "workspace:~",
"@typespec/versioning": "workspace:~",
"@vitest/coverage-v8": "^3.0.3",
"@vitest/ui": "^3.0.3",
"c8": "^10.1.3",
Expand Down
143 changes: 143 additions & 0 deletions packages/http-server-csharp/src/cli/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/* eslint-disable no-console */
import { resolvePath, typespecVersion } from "@typespec/compiler";
import { spawn } from "cross-spawn";
import pc from "picocolors";
import yargs from "yargs";
import { hideBin } from "yargs/helpers";

async function main() {
console.log(`TypeSpec Http Server Emitter for C-Sharp v${typespecVersion}\n`);

await yargs(hideBin(process.argv))
.scriptName("hscs")
.help()
.strict()
.parserConfiguration({
"greedy-arrays": false,
"boolean-negation": false,
})
.command(
"scaffold [--use-swaggerui] <project-directory> <path-to-spec>",
"Generate a complete project with mock implementation at the given project-directory for the given spec. This required dotnet 9: https://dotnet.microsoft.com/download.",
(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,
})
.positional("project-directory", {
description: "Path to the directory where the project will be created.",
type: "string",
demandOption: true,
})
.positional("path-to-spec", {
description: "The path to the spec to generate a project for",
type: "string",
demandOption: true,
});
},
async (args) => {
const projectDir = resolvePath(process.cwd(), args["project-directory"]);
const pathToSpec = resolvePath(process.cwd(), args["path-to-spec"]);
const useSwagger: boolean = args["use-swaggerui"];
console.log(pc.bold("Generating new Project"));
let result = await runScriptAsync("dotnet", ["new", "web", "-o", projectDir, "--force"]);
if (result !== 0) {
console.log(
pc.bold(
"Dotnet version 9 cli is required for scaffolding. Download here: https://dotnet.microsoft.com/download ",
),
);
return;
}
if (useSwagger) {
console.log(pc.bold("Adding OpenApi generation support"));
console.log(pc.green(`> dotnet add ${projectDir} package SwashBuckle.AspNetCore`));
result = await runScriptAsync("dotnet", [
"add",
projectDir,
"package",
"SwashBuckle.AspNetCore",
]);
}
console.log(pc.bold("Compiling spec with mock implementations"));
const generatedTargetDir = resolvePath(process.cwd(), projectDir, "generated");
const generatedOpenApiDir = resolvePath(process.cwd(), projectDir, "openapi");
const compileArgs: string[] = [
"tsp",
"compile",
pathToSpec,
"--emit",
"@typespec/http-server-csharp",
"--option",
`@typespec/http-server-csharp.emitter-output-dir=${generatedTargetDir}`,
"--option",
"@typespec/http-server-csharp.emit-mocks=all",
];

const swaggerArgs: string[] = [
"--emit",
"@typespec/openapi3",
"--option",
`@typespec/openapi3.emitter-output-dir=${generatedOpenApiDir}`,
"--option",
"@typespec/http-server-csharp.use-swaggerui=true",
];
if (useSwagger) compileArgs.push(...swaggerArgs);
result = await runScriptAsync("npx", compileArgs);
if (result === 0) {
console.log(pc.bold(`Your project was successfully created at "${projectDir}"`));
console.log(
`You can build and start the project using 'dotnet run --project ${projectDir}'`,
);
} 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. ",
);
}
}
},
)
.version(typespecVersion)
.demandCommand(1, "You must use one of the supported commands.").argv;
}

function internalError(error: unknown) {
// 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 runScriptAsync(cmd: string, args: string[]): Promise<number> {
let resolver: (value: number | PromiseLike<number>) => void;
let rejecter: (value: unknown) => void;
const promise = new Promise<number>((resolve, reject) => {
resolver = resolve;
rejecter = reject;
});
console.log(pc.green(`> ${cmd} ${args.join(" ")}`));
const proc = spawn(cmd, args);
proc.stdout?.on("data", (data) => console.log(pc.dim(data)));
proc.stderr?.on("data", (data) => {
console.error(data);
rejecter(data);
});
proc.on("close", (_, __) => {
resolver(proc.exitCode || 0);
});

return promise;
}

process.on("unhandledRejection", (error: unknown) => {
console.error("Unhandled promise rejection!");
internalError(error);
});

main().catch(internalError);
Loading

0 comments on commit 5c7da4f

Please sign in to comment.