@tsonic/backend 0.0.1 → 0.0.2

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.
Files changed (47) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/build-orchestrator.d.ts +9 -0
  3. package/dist/build-orchestrator.d.ts.map +1 -0
  4. package/dist/build-orchestrator.js +165 -0
  5. package/dist/build-orchestrator.js.map +1 -0
  6. package/dist/dotnet.d.ts +17 -0
  7. package/dist/dotnet.d.ts.map +1 -0
  8. package/dist/dotnet.js +85 -0
  9. package/dist/dotnet.js.map +1 -0
  10. package/dist/dotnet.test.d.ts +5 -0
  11. package/dist/dotnet.test.d.ts.map +1 -0
  12. package/dist/dotnet.test.js +46 -0
  13. package/dist/dotnet.test.js.map +1 -0
  14. package/dist/index.d.ts +9 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/{src/index.ts → dist/index.js} +1 -19
  17. package/dist/index.js.map +1 -0
  18. package/dist/program-generator.d.ts +9 -0
  19. package/dist/program-generator.d.ts.map +1 -0
  20. package/dist/program-generator.js +27 -0
  21. package/dist/program-generator.js.map +1 -0
  22. package/dist/program-generator.test.d.ts +5 -0
  23. package/dist/program-generator.test.d.ts.map +1 -0
  24. package/dist/program-generator.test.js +53 -0
  25. package/dist/program-generator.test.js.map +1 -0
  26. package/dist/project-generator.d.ts +18 -0
  27. package/dist/project-generator.d.ts.map +1 -0
  28. package/dist/project-generator.js +182 -0
  29. package/dist/project-generator.js.map +1 -0
  30. package/dist/project-generator.test.d.ts +5 -0
  31. package/dist/project-generator.test.d.ts.map +1 -0
  32. package/dist/project-generator.test.js +103 -0
  33. package/dist/project-generator.test.js.map +1 -0
  34. package/dist/types.d.ts +133 -0
  35. package/dist/types.d.ts.map +1 -0
  36. package/dist/types.js +5 -0
  37. package/dist/types.js.map +1 -0
  38. package/package.json +6 -3
  39. package/src/build-orchestrator.ts +0 -215
  40. package/src/dotnet.test.ts +0 -50
  41. package/src/dotnet.ts +0 -101
  42. package/src/program-generator.test.ts +0 -63
  43. package/src/program-generator.ts +0 -33
  44. package/src/project-generator.test.ts +0 -130
  45. package/src/project-generator.ts +0 -245
  46. package/src/types.ts +0 -150
  47. package/tsconfig.json +0 -14
@@ -1,215 +0,0 @@
1
- /**
2
- * Main build orchestration - coordinates the entire NativeAOT build process
3
- */
4
-
5
- import { createHash } from "crypto";
6
- import {
7
- mkdirSync,
8
- writeFileSync,
9
- copyFileSync,
10
- rmSync,
11
- chmodSync,
12
- existsSync,
13
- readdirSync,
14
- } from "fs";
15
- import { join, dirname } from "path";
16
- import {
17
- BuildOptions,
18
- BuildResult,
19
- BuildConfig,
20
- EntryInfo,
21
- NuGetPackage,
22
- } from "./types.js";
23
- import { generateCsproj } from "./project-generator.js";
24
- import { generateProgramCs } from "./program-generator.js";
25
- import { checkDotnetInstalled, detectRid, publishNativeAot } from "./dotnet.js";
26
-
27
- /**
28
- * Create unique build directory
29
- */
30
- const createBuildDir = (entryFile: string): string => {
31
- const hash = createHash("md5").update(entryFile).digest("hex").slice(0, 8);
32
- const buildDir = join(process.cwd(), ".tsonic", "build", hash);
33
- mkdirSync(buildDir, { recursive: true });
34
- return buildDir;
35
- };
36
-
37
- /**
38
- * Copy generated C# files to build directory
39
- */
40
- const copyGeneratedFiles = (
41
- emittedFiles: Map<string, string>,
42
- buildDir: string
43
- ): void => {
44
- for (const [tsPath, csContent] of emittedFiles) {
45
- // src/models/User.ts → <buildDir>/src/models/User.cs
46
- const csPath = tsPath.replace(/\.ts$/, ".cs");
47
- const fullPath = join(buildDir, csPath);
48
-
49
- mkdirSync(dirname(fullPath), { recursive: true });
50
- writeFileSync(fullPath, csContent, "utf-8");
51
- }
52
- };
53
-
54
- /**
55
- * Find project .csproj file in current directory
56
- */
57
- const findProjectCsproj = (): string | null => {
58
- const cwd = process.cwd();
59
- // Look for *.csproj in current directory
60
- const files = readdirSync(cwd);
61
- const csprojFile = files.find((f) => f.endsWith(".csproj"));
62
- return csprojFile ? join(cwd, csprojFile) : null;
63
- };
64
-
65
- /**
66
- * Get output binary name for platform
67
- */
68
- const getOutputBinaryName = (outputName: string): string => {
69
- return process.platform === "win32" ? `${outputName}.exe` : outputName;
70
- };
71
-
72
- /**
73
- * Copy output binary to final location
74
- */
75
- const copyOutputBinary = (
76
- buildDir: string,
77
- rid: string,
78
- outputPath: string,
79
- outputName: string
80
- ): void => {
81
- const publishDir = join(buildDir, "bin/Release/net8.0", rid, "publish");
82
- const binaryName = getOutputBinaryName(outputName);
83
- const binaryPath = join(publishDir, binaryName);
84
-
85
- if (!existsSync(binaryPath)) {
86
- throw new Error(`Output binary not found at ${binaryPath}`);
87
- }
88
-
89
- copyFileSync(binaryPath, outputPath);
90
-
91
- // Make executable on Unix
92
- if (process.platform !== "win32") {
93
- chmodSync(outputPath, 0o755);
94
- }
95
- };
96
-
97
- /**
98
- * Clean build directory
99
- */
100
- const cleanBuild = (buildDir: string, keepTemp: boolean): void => {
101
- if (!keepTemp) {
102
- rmSync(buildDir, { recursive: true, force: true });
103
- }
104
- };
105
-
106
- /**
107
- * Build NativeAOT executable from C# files
108
- */
109
- export const buildNativeAot = (
110
- emittedFiles: Map<string, string>,
111
- entryInfo: EntryInfo,
112
- options: BuildOptions
113
- ): BuildResult => {
114
- let buildDir: string | undefined;
115
-
116
- try {
117
- // Check dotnet is installed
118
- const dotnetCheck = checkDotnetInstalled();
119
- if (!dotnetCheck.ok) {
120
- return {
121
- ok: false,
122
- error: dotnetCheck.error,
123
- };
124
- }
125
-
126
- // Create build directory
127
- buildDir = createBuildDir(Array.from(emittedFiles.keys())[0] || "main");
128
-
129
- // Copy generated C# files
130
- copyGeneratedFiles(emittedFiles, buildDir);
131
-
132
- // Generate Program.cs if needed
133
- if (entryInfo.needsProgram) {
134
- const programCs = generateProgramCs(entryInfo);
135
- writeFileSync(join(buildDir, "Program.cs"), programCs, "utf-8");
136
- }
137
-
138
- // Use existing project .csproj if available, otherwise generate one
139
- const projectCsproj = findProjectCsproj();
140
- if (projectCsproj) {
141
- // Copy existing .csproj to build directory (preserves user edits)
142
- const csprojFilename = projectCsproj.split("/").pop() || "project.csproj";
143
- copyFileSync(projectCsproj, join(buildDir, csprojFilename));
144
- } else {
145
- // Generate temporary .csproj for build
146
- // Tsonic.Runtime is ALWAYS required (for unions, typeof, structural)
147
- // Tsonic.JSRuntime only when mode: "js" (for JS semantics)
148
- const packages: NuGetPackage[] = [
149
- { name: "Tsonic.Runtime", version: "0.0.1" },
150
- ];
151
- if (entryInfo.runtime === "js") {
152
- packages.push({ name: "Tsonic.JSRuntime", version: "0.0.1" });
153
- }
154
-
155
- const buildConfig: BuildConfig = {
156
- rootNamespace: options.namespace,
157
- outputName: options.outputName || "tsonic",
158
- dotnetVersion: options.dotnetVersion || "net10.0",
159
- packages,
160
- outputConfig: {
161
- type: "executable",
162
- nativeAot: true,
163
- singleFile: true,
164
- trimmed: true,
165
- stripSymbols: options.stripSymbols ?? true,
166
- optimization: options.optimizationPreference || "Speed",
167
- invariantGlobalization: true,
168
- selfContained: true,
169
- },
170
- };
171
-
172
- const csprojContent = generateCsproj(buildConfig);
173
- writeFileSync(join(buildDir, "tsonic.csproj"), csprojContent, "utf-8");
174
- }
175
-
176
- // Detect RID
177
- const rid = options.rid || detectRid();
178
-
179
- // Execute dotnet publish
180
- const publishResult = publishNativeAot(buildDir, rid);
181
- if (!publishResult.ok) {
182
- return {
183
- ok: false,
184
- error: publishResult.error,
185
- buildDir,
186
- };
187
- }
188
-
189
- // Copy output binary
190
- const outputPath = options.outputName
191
- ? `./${getOutputBinaryName(options.outputName)}`
192
- : "./tsonic-app";
193
- copyOutputBinary(buildDir, rid, outputPath, options.outputName || "tsonic");
194
-
195
- // Cleanup if requested
196
- cleanBuild(buildDir, options.keepTemp ?? false);
197
-
198
- return {
199
- ok: true,
200
- outputPath,
201
- buildDir,
202
- };
203
- } catch (error) {
204
- if (buildDir) {
205
- cleanBuild(buildDir, false);
206
- }
207
-
208
- return {
209
- ok: false,
210
- error:
211
- error instanceof Error ? error.message : "Unknown build error occurred",
212
- buildDir,
213
- };
214
- }
215
- };
@@ -1,50 +0,0 @@
1
- /**
2
- * Tests for dotnet CLI wrapper
3
- */
4
-
5
- import { describe, it } from "mocha";
6
- import { expect } from "chai";
7
- import { detectRid } from "./dotnet.js";
8
-
9
- describe("Dotnet CLI Wrapper", () => {
10
- describe("detectRid", () => {
11
- it("should return a valid runtime identifier", () => {
12
- const rid = detectRid();
13
-
14
- // Should match one of the known RID patterns
15
- const validRids = [
16
- "osx-x64",
17
- "osx-arm64",
18
- "linux-x64",
19
- "linux-arm64",
20
- "win-x64",
21
- "win-arm64",
22
- ];
23
-
24
- expect(validRids).to.include(rid);
25
- });
26
-
27
- it("should detect current platform RID", () => {
28
- const rid = detectRid();
29
- const platform = process.platform;
30
- const arch = process.arch;
31
-
32
- if (platform === "darwin") {
33
- expect(rid).to.match(/^osx-/);
34
- } else if (platform === "linux") {
35
- expect(rid).to.match(/^linux-/);
36
- } else if (platform === "win32") {
37
- expect(rid).to.match(/^win-/);
38
- }
39
-
40
- if (arch === "x64") {
41
- expect(rid).to.include("x64");
42
- } else if (arch === "arm64") {
43
- expect(rid).to.include("arm64");
44
- }
45
- });
46
- });
47
-
48
- // Note: checkDotnetInstalled and publishNativeAot are integration tests
49
- // that require dotnet to be installed, so we skip them in unit tests
50
- });
package/src/dotnet.ts DELETED
@@ -1,101 +0,0 @@
1
- /**
2
- * Dotnet CLI wrapper for executing build commands
3
- */
4
-
5
- import { spawnSync } from "child_process";
6
- import { DotnetResult } from "./types.js";
7
-
8
- /**
9
- * Check if dotnet CLI is available
10
- */
11
- export const checkDotnetInstalled = (): DotnetResult => {
12
- const result = spawnSync("dotnet", ["--version"], {
13
- encoding: "utf-8",
14
- });
15
-
16
- if (result.error) {
17
- return {
18
- ok: false,
19
- error: ".NET SDK not found. Install from https://dot.net",
20
- };
21
- }
22
-
23
- if (result.status !== 0) {
24
- return {
25
- ok: false,
26
- error: "dotnet command failed",
27
- stderr: result.stderr,
28
- };
29
- }
30
-
31
- return {
32
- ok: true,
33
- stdout: result.stdout.trim(),
34
- };
35
- };
36
-
37
- /**
38
- * Detect runtime identifier for current platform
39
- */
40
- export const detectRid = (): string => {
41
- const platform = process.platform;
42
- const arch = process.arch;
43
-
44
- const ridMap: Record<string, string> = {
45
- "darwin-x64": "osx-x64",
46
- "darwin-arm64": "osx-arm64",
47
- "linux-x64": "linux-x64",
48
- "linux-arm64": "linux-arm64",
49
- "win32-x64": "win-x64",
50
- "win32-arm64": "win-arm64",
51
- };
52
-
53
- const key = `${platform}-${arch}`;
54
- return ridMap[key] || "linux-x64";
55
- };
56
-
57
- /**
58
- * Execute dotnet publish with NativeAOT
59
- */
60
- export const publishNativeAot = (
61
- buildDir: string,
62
- rid: string
63
- ): DotnetResult => {
64
- const args = [
65
- "publish",
66
- "tsonic.csproj",
67
- "-c",
68
- "Release",
69
- "-r",
70
- rid,
71
- "-p:PublishAot=true",
72
- "-p:PublishSingleFile=true",
73
- "--self-contained",
74
- ];
75
-
76
- const result = spawnSync("dotnet", args, {
77
- cwd: buildDir,
78
- encoding: "utf-8",
79
- });
80
-
81
- if (result.error) {
82
- return {
83
- ok: false,
84
- error: `Failed to execute dotnet: ${result.error.message}`,
85
- };
86
- }
87
-
88
- if (result.status !== 0) {
89
- return {
90
- ok: false,
91
- error: `dotnet publish failed with code ${result.status}`,
92
- stdout: result.stdout,
93
- stderr: result.stderr,
94
- };
95
- }
96
-
97
- return {
98
- ok: true,
99
- stdout: result.stdout,
100
- };
101
- };
@@ -1,63 +0,0 @@
1
- /**
2
- * Tests for Program.cs generation
3
- */
4
-
5
- import { describe, it } from "mocha";
6
- import { expect } from "chai";
7
- import { generateProgramCs } from "./program-generator.js";
8
- import { EntryInfo } from "./types.js";
9
-
10
- describe("Program Generator", () => {
11
- describe("generateProgramCs", () => {
12
- it("should generate synchronous Main method", () => {
13
- const entryInfo: EntryInfo = {
14
- namespace: "MyApp",
15
- className: "main",
16
- methodName: "start",
17
- isAsync: false,
18
- needsProgram: true,
19
- };
20
-
21
- const result = generateProgramCs(entryInfo);
22
-
23
- expect(result).to.include("public static void Main(string[] args)");
24
- expect(result).to.include("main.start();");
25
- expect(result).to.not.include("await");
26
- expect(result).to.not.include("async");
27
- expect(result).to.include("using MyApp;");
28
- });
29
-
30
- it("should generate async Main method", () => {
31
- const entryInfo: EntryInfo = {
32
- namespace: "MyApp.Services",
33
- className: "main",
34
- methodName: "run",
35
- isAsync: true,
36
- needsProgram: true,
37
- };
38
-
39
- const result = generateProgramCs(entryInfo);
40
-
41
- expect(result).to.include("public static async Task Main(string[] args)");
42
- expect(result).to.include("await main.run();");
43
- expect(result).to.include("using MyApp.Services;");
44
- expect(result).to.include("using System.Threading.Tasks;");
45
- });
46
-
47
- it("should include required using statements", () => {
48
- const entryInfo: EntryInfo = {
49
- namespace: "Test",
50
- className: "Program",
51
- methodName: "Main",
52
- isAsync: false,
53
- needsProgram: true,
54
- };
55
-
56
- const result = generateProgramCs(entryInfo);
57
-
58
- expect(result).to.include("using System;");
59
- expect(result).to.include("using Tsonic.Runtime;");
60
- expect(result).to.include("using Test;");
61
- });
62
- });
63
- });
@@ -1,33 +0,0 @@
1
- /**
2
- * Program.cs generation for entry point wrapper
3
- */
4
-
5
- import { EntryInfo } from "./types.js";
6
-
7
- /**
8
- * Generate Program.cs content with Main method
9
- */
10
- export const generateProgramCs = (entryInfo: EntryInfo): string => {
11
- const returnType = entryInfo.isAsync ? "async Task" : "void";
12
- const awaitKeyword = entryInfo.isAsync ? "await " : "";
13
-
14
- const usings = ["using System;", "using System.Threading.Tasks;"];
15
-
16
- // Only include Tsonic.Runtime for js runtime mode
17
- if (entryInfo.runtime !== "dotnet") {
18
- usings.push("using Tsonic.Runtime;");
19
- }
20
-
21
- usings.push(`using ${entryInfo.namespace};`);
22
-
23
- return `${usings.join("\n")}
24
-
25
- public static class Program
26
- {
27
- public static ${returnType} Main(string[] args)
28
- {
29
- ${awaitKeyword}${entryInfo.className}.${entryInfo.methodName}();
30
- }
31
- }
32
- `;
33
- };
@@ -1,130 +0,0 @@
1
- /**
2
- * Tests for .csproj generation
3
- */
4
-
5
- import { describe, it } from "mocha";
6
- import { expect } from "chai";
7
- import { generateCsproj } from "./project-generator.js";
8
- import { BuildConfig } from "./types.js";
9
-
10
- describe("Project Generator", () => {
11
- describe("generateCsproj", () => {
12
- it("should generate basic executable .csproj without packages", () => {
13
- const config: BuildConfig = {
14
- rootNamespace: "TestApp",
15
- outputName: "test",
16
- dotnetVersion: "net10.0",
17
- packages: [],
18
- outputConfig: {
19
- type: "executable",
20
- nativeAot: true,
21
- singleFile: true,
22
- trimmed: true,
23
- stripSymbols: true,
24
- optimization: "Speed",
25
- invariantGlobalization: true,
26
- selfContained: true,
27
- },
28
- };
29
-
30
- const result = generateCsproj(config);
31
-
32
- expect(result).to.include('<Project Sdk="Microsoft.NET.Sdk">');
33
- expect(result).to.include("<TargetFramework>net10.0</TargetFramework>");
34
- expect(result).to.include("<RootNamespace>TestApp</RootNamespace>");
35
- expect(result).to.include("<AssemblyName>test</AssemblyName>");
36
- expect(result).to.include("<PublishAot>true</PublishAot>");
37
- expect(result).to.include(
38
- "<OptimizationPreference>Speed</OptimizationPreference>"
39
- );
40
- });
41
-
42
- it("should include package references when provided", () => {
43
- const config: BuildConfig = {
44
- rootNamespace: "TestApp",
45
- outputName: "test",
46
- dotnetVersion: "net10.0",
47
- packages: [
48
- { name: "System.Text.Json", version: "8.0.0" },
49
- { name: "Newtonsoft.Json", version: "13.0.3" },
50
- ],
51
- outputConfig: {
52
- type: "executable",
53
- nativeAot: true,
54
- singleFile: true,
55
- trimmed: true,
56
- stripSymbols: true,
57
- optimization: "Size",
58
- invariantGlobalization: true,
59
- selfContained: true,
60
- },
61
- };
62
-
63
- const result = generateCsproj(config);
64
-
65
- expect(result).to.include(
66
- '<PackageReference Include="System.Text.Json" Version="8.0.0"'
67
- );
68
- expect(result).to.include(
69
- '<PackageReference Include="Newtonsoft.Json" Version="13.0.3"'
70
- );
71
- expect(result).to.include(
72
- "<OptimizationPreference>Size</OptimizationPreference>"
73
- );
74
- });
75
-
76
- it("should set invariant globalization correctly", () => {
77
- const config: BuildConfig = {
78
- rootNamespace: "TestApp",
79
- outputName: "test",
80
- dotnetVersion: "net10.0",
81
- packages: [],
82
- outputConfig: {
83
- type: "executable",
84
- nativeAot: true,
85
- singleFile: true,
86
- trimmed: true,
87
- stripSymbols: false,
88
- optimization: "Speed",
89
- invariantGlobalization: false,
90
- selfContained: true,
91
- },
92
- };
93
-
94
- const result = generateCsproj(config);
95
-
96
- expect(result).to.include(
97
- "<InvariantGlobalization>false</InvariantGlobalization>"
98
- );
99
- expect(result).to.include("<StripSymbols>false</StripSymbols>");
100
- });
101
-
102
- it("should generate library .csproj", () => {
103
- const config: BuildConfig = {
104
- rootNamespace: "TestLib",
105
- outputName: "testlib",
106
- dotnetVersion: "net10.0",
107
- packages: [],
108
- outputConfig: {
109
- type: "library",
110
- targetFrameworks: ["net8.0", "net9.0"],
111
- generateDocumentation: true,
112
- includeSymbols: true,
113
- packable: false,
114
- },
115
- };
116
-
117
- const result = generateCsproj(config);
118
-
119
- expect(result).to.include("<OutputType>Library</OutputType>");
120
- expect(result).to.include(
121
- "<TargetFrameworks>net8.0;net9.0</TargetFrameworks>"
122
- );
123
- expect(result).to.include(
124
- "<GenerateDocumentationFile>true</GenerateDocumentationFile>"
125
- );
126
- expect(result).to.include("<DebugType>embedded</DebugType>");
127
- expect(result).to.include("<IsPackable>false</IsPackable>");
128
- });
129
- });
130
- });