tsonic 0.0.1 → 0.0.12

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 (79) hide show
  1. package/README.md +21 -0
  2. package/bin.js +17 -0
  3. package/package.json +16 -29
  4. package/dist/cli/constants.d.ts +0 -5
  5. package/dist/cli/constants.d.ts.map +0 -1
  6. package/dist/cli/constants.js +0 -5
  7. package/dist/cli/constants.js.map +0 -1
  8. package/dist/cli/dispatcher.d.ts +0 -8
  9. package/dist/cli/dispatcher.d.ts.map +0 -1
  10. package/dist/cli/dispatcher.js +0 -98
  11. package/dist/cli/dispatcher.js.map +0 -1
  12. package/dist/cli/help.d.ts +0 -8
  13. package/dist/cli/help.d.ts.map +0 -1
  14. package/dist/cli/help.js +0 -48
  15. package/dist/cli/help.js.map +0 -1
  16. package/dist/cli/index.d.ts +0 -8
  17. package/dist/cli/index.d.ts.map +0 -1
  18. package/dist/cli/index.js +0 -8
  19. package/dist/cli/index.js.map +0 -1
  20. package/dist/cli/parser.d.ts +0 -14
  21. package/dist/cli/parser.d.ts.map +0 -1
  22. package/dist/cli/parser.js +0 -94
  23. package/dist/cli/parser.js.map +0 -1
  24. package/dist/cli/parser.test.d.ts +0 -5
  25. package/dist/cli/parser.test.d.ts.map +0 -1
  26. package/dist/cli/parser.test.js +0 -221
  27. package/dist/cli/parser.test.js.map +0 -1
  28. package/dist/cli.d.ts +0 -6
  29. package/dist/cli.d.ts.map +0 -1
  30. package/dist/cli.js +0 -6
  31. package/dist/cli.js.map +0 -1
  32. package/dist/commands/build.d.ts +0 -11
  33. package/dist/commands/build.d.ts.map +0 -1
  34. package/dist/commands/build.js +0 -99
  35. package/dist/commands/build.js.map +0 -1
  36. package/dist/commands/emit.d.ts +0 -12
  37. package/dist/commands/emit.d.ts.map +0 -1
  38. package/dist/commands/emit.js +0 -170
  39. package/dist/commands/emit.js.map +0 -1
  40. package/dist/commands/init.d.ts +0 -14
  41. package/dist/commands/init.d.ts.map +0 -1
  42. package/dist/commands/init.js +0 -217
  43. package/dist/commands/init.js.map +0 -1
  44. package/dist/commands/run.d.ts +0 -11
  45. package/dist/commands/run.d.ts.map +0 -1
  46. package/dist/commands/run.js +0 -40
  47. package/dist/commands/run.js.map +0 -1
  48. package/dist/config.d.ts +0 -17
  49. package/dist/config.d.ts.map +0 -1
  50. package/dist/config.js +0 -84
  51. package/dist/config.js.map +0 -1
  52. package/dist/config.test.d.ts +0 -5
  53. package/dist/config.test.d.ts.map +0 -1
  54. package/dist/config.test.js +0 -274
  55. package/dist/config.test.js.map +0 -1
  56. package/dist/index.d.ts +0 -8
  57. package/dist/index.d.ts.map +0 -1
  58. package/dist/index.js +0 -20
  59. package/dist/index.js.map +0 -1
  60. package/dist/types.d.ts +0 -74
  61. package/dist/types.d.ts.map +0 -1
  62. package/dist/types.js +0 -5
  63. package/dist/types.js.map +0 -1
  64. package/src/cli/constants.ts +0 -5
  65. package/src/cli/dispatcher.ts +0 -115
  66. package/src/cli/help.ts +0 -49
  67. package/src/cli/index.ts +0 -8
  68. package/src/cli/parser.test.ts +0 -259
  69. package/src/cli/parser.ts +0 -109
  70. package/src/cli.ts +0 -6
  71. package/src/commands/build.ts +0 -125
  72. package/src/commands/emit.ts +0 -222
  73. package/src/commands/init.ts +0 -272
  74. package/src/commands/run.ts +0 -51
  75. package/src/config.test.ts +0 -328
  76. package/src/config.ts +0 -105
  77. package/src/index.ts +0 -23
  78. package/src/types.ts +0 -74
  79. package/tsconfig.json +0 -18
@@ -1,222 +0,0 @@
1
- /**
2
- * tsonic emit command - Generate C# code only
3
- */
4
-
5
- import { mkdirSync, writeFileSync, existsSync } from "node:fs";
6
- import { join, dirname, relative, resolve } from "node:path";
7
- import {
8
- compile,
9
- buildIr,
10
- type Diagnostic,
11
- type IrModule,
12
- } from "@tsonic/frontend";
13
- import { emitCSharpFiles } from "@tsonic/emitter";
14
- import {
15
- generateCsproj,
16
- generateProgramCs,
17
- type EntryInfo,
18
- } from "@tsonic/backend";
19
- import type { ResolvedConfig, Result } from "../types.js";
20
-
21
- /**
22
- * Extract entry point information from IR module
23
- */
24
- const extractEntryInfo = (entryModule: IrModule): EntryInfo | null => {
25
- // Look for exported 'main' function
26
- for (const exp of entryModule.exports) {
27
- if (exp.kind === "declaration") {
28
- const decl = exp.declaration;
29
- if (decl.kind === "functionDeclaration" && decl.name === "main") {
30
- return {
31
- namespace: entryModule.namespace,
32
- className: entryModule.className,
33
- methodName: "main",
34
- isAsync: decl.isAsync,
35
- needsProgram: true,
36
- };
37
- }
38
- } else if (exp.kind === "named" && exp.name === "main") {
39
- // Named export of 'main'
40
- // Look in body for the function declaration
41
- for (const stmt of entryModule.body) {
42
- if (stmt.kind === "functionDeclaration" && stmt.name === "main") {
43
- return {
44
- namespace: entryModule.namespace,
45
- className: entryModule.className,
46
- methodName: "main",
47
- isAsync: stmt.isAsync,
48
- needsProgram: true,
49
- };
50
- }
51
- }
52
- }
53
- }
54
-
55
- // No main function found
56
- return null;
57
- };
58
-
59
- /**
60
- * Emit C# code from TypeScript
61
- */
62
- export const emitCommand = (
63
- config: ResolvedConfig
64
- ): Result<{ filesGenerated: number; outputDir: string }, string> => {
65
- const {
66
- entryPoint,
67
- outputDirectory,
68
- rootNamespace,
69
- sourceRoot,
70
- packages,
71
- typeRoots,
72
- } = config;
73
-
74
- if (!config.quiet) {
75
- console.log(`Emitting C# code for ${entryPoint}...`);
76
- }
77
-
78
- try {
79
- // Parse TypeScript
80
- const compileResult = compile([entryPoint], {
81
- sourceRoot,
82
- rootNamespace,
83
- typeRoots,
84
- });
85
- if (!compileResult.ok) {
86
- return {
87
- ok: false,
88
- error: `TypeScript compilation failed:\n${compileResult.error.diagnostics.map((d: Diagnostic) => d.message).join("\n")}`,
89
- };
90
- }
91
-
92
- // Build IR
93
- const irResult = buildIr(compileResult.value.program, {
94
- sourceRoot,
95
- rootNamespace,
96
- });
97
-
98
- if (!irResult.ok) {
99
- return {
100
- ok: false,
101
- error: `IR build failed:\n${irResult.error.map((d: Diagnostic) => d.message).join("\n")}`,
102
- };
103
- }
104
-
105
- // Emit C# code
106
- const absoluteEntryPoint = resolve(entryPoint);
107
- const csFiles = emitCSharpFiles(irResult.value, {
108
- rootNamespace,
109
- entryPointPath: absoluteEntryPoint,
110
- });
111
-
112
- // Create output directory
113
- const outputDir = join(process.cwd(), outputDirectory);
114
- mkdirSync(outputDir, { recursive: true });
115
-
116
- // Write C# files preserving directory structure
117
- for (const [modulePath, csCode] of csFiles) {
118
- // Convert module path to C# file path
119
- // src/models/User.ts → generated/src/models/User.cs
120
- const csPath = modulePath.replace(/\.ts$/, ".cs");
121
- const fullPath = join(outputDir, csPath);
122
-
123
- mkdirSync(dirname(fullPath), { recursive: true });
124
- writeFileSync(fullPath, csCode, "utf-8");
125
-
126
- if (config.verbose) {
127
- const relPath = relative(process.cwd(), fullPath);
128
- console.log(` Generated: ${relPath}`);
129
- }
130
- }
131
-
132
- // Generate Program.cs entry point wrapper
133
- const entryModule = irResult.value.find(
134
- (m) => resolve(m.filePath) === absoluteEntryPoint
135
- );
136
-
137
- if (entryModule) {
138
- const entryInfo = extractEntryInfo(entryModule);
139
- if (entryInfo) {
140
- const programCs = generateProgramCs(entryInfo);
141
- const programPath = join(outputDir, "Program.cs");
142
- writeFileSync(programPath, programCs, "utf-8");
143
-
144
- if (config.verbose) {
145
- console.log(` Generated: ${relative(process.cwd(), programPath)}`);
146
- }
147
- }
148
- }
149
-
150
- // Generate or preserve tsonic.csproj
151
- const csprojPath = join(outputDir, "tsonic.csproj");
152
- if (!existsSync(csprojPath)) {
153
- // Find Tsonic.Runtime.csproj path - try multiple locations
154
- let runtimePath: string | undefined;
155
-
156
- // 1. Try monorepo structure (development)
157
- const monorepoPath = resolve(
158
- join(import.meta.dirname, "../../../runtime/src/Tsonic.Runtime.csproj")
159
- );
160
- if (existsSync(monorepoPath)) {
161
- runtimePath = monorepoPath;
162
- } else {
163
- // 2. Try installed package structure
164
- const installedPath = resolve(
165
- join(
166
- import.meta.dirname,
167
- "../../../../@tsonic/runtime/src/Tsonic.Runtime.csproj"
168
- )
169
- );
170
- if (existsSync(installedPath)) {
171
- runtimePath = installedPath;
172
- }
173
- }
174
-
175
- // Warn if no runtime found
176
- if (!runtimePath && !config.quiet) {
177
- console.warn(
178
- "Warning: Tsonic.Runtime.csproj not found. You may need to add a reference manually."
179
- );
180
- }
181
-
182
- const csproj = generateCsproj({
183
- rootNamespace,
184
- outputName: config.outputName,
185
- dotnetVersion: config.dotnetVersion,
186
- runtimePath,
187
- packages,
188
- invariantGlobalization: config.invariantGlobalization,
189
- stripSymbols: config.stripSymbols,
190
- optimizationPreference: config.optimize === "size" ? "Size" : "Speed",
191
- });
192
- writeFileSync(csprojPath, csproj, "utf-8");
193
-
194
- if (config.verbose) {
195
- console.log(` Generated: ${relative(process.cwd(), csprojPath)}`);
196
- }
197
- } else if (config.verbose) {
198
- console.log(
199
- ` Preserved: ${relative(process.cwd(), csprojPath)} (user edits kept)`
200
- );
201
- }
202
-
203
- if (!config.quiet) {
204
- console.log(
205
- `\n✓ Generated ${csFiles.size} C# files in ${outputDirectory}/`
206
- );
207
- }
208
-
209
- return {
210
- ok: true,
211
- value: {
212
- filesGenerated: csFiles.size,
213
- outputDir,
214
- },
215
- };
216
- } catch (error) {
217
- return {
218
- ok: false,
219
- error: `Emit failed: ${error instanceof Error ? error.message : String(error)}`,
220
- };
221
- }
222
- };
@@ -1,272 +0,0 @@
1
- /**
2
- * tsonic project init command
3
- */
4
-
5
- import { writeFileSync, existsSync, readFileSync, mkdirSync } from "node:fs";
6
- import { join } from "node:path";
7
- import { spawnSync } from "node:child_process";
8
- import type { Result } from "../types.js";
9
-
10
- type InitOptions = {
11
- readonly skipTypes?: boolean;
12
- readonly typesVersion?: string;
13
- };
14
-
15
- const DEFAULT_GITIGNORE = `# .NET build artifacts
16
- generated/bin/
17
- generated/obj/
18
-
19
- # Optional: Uncomment to ignore generated C# files
20
- # generated/**/*.cs
21
-
22
- # Output executables
23
- *.exe
24
- app
25
-
26
- # Dependencies
27
- node_modules/
28
- `;
29
-
30
- const SAMPLE_MAIN_TS = `import { Console } from "System";
31
- import { File } from "System.IO";
32
-
33
- export function main(): void {
34
- Console.WriteLine("Reading README.md...");
35
- const content = File.ReadAllText("./README.md");
36
- Console.WriteLine(content);
37
- }
38
- `;
39
-
40
- const SAMPLE_README = `# My Tsonic Project
41
-
42
- This is a sample Tsonic project that demonstrates .NET interop.
43
-
44
- ## Getting Started
45
-
46
- Build and run the project:
47
-
48
- \`\`\`bash
49
- npm run build
50
- ./app
51
- \`\`\`
52
-
53
- Or run directly:
54
-
55
- \`\`\`bash
56
- npm run dev
57
- \`\`\`
58
-
59
- ## Project Structure
60
-
61
- - \`src/main.ts\` - Entry point
62
- - \`tsonic.json\` - Project configuration
63
- - \`generated/\` - Generated C# code (gitignored)
64
- `;
65
-
66
- /**
67
- * Generate tsonic.json config
68
- */
69
- const generateConfig = (includeTypeRoots: boolean): string => {
70
- const config: Record<string, unknown> = {
71
- $schema: "https://tsonic.dev/schema/v1.json",
72
- rootNamespace: "MyApp",
73
- entryPoint: "src/main.ts",
74
- sourceRoot: "src",
75
- outputDirectory: "generated",
76
- outputName: "app",
77
- optimize: "speed",
78
- packages: [],
79
- buildOptions: {
80
- stripSymbols: true,
81
- invariantGlobalization: true,
82
- },
83
- };
84
-
85
- if (includeTypeRoots) {
86
- config.dotnet = {
87
- typeRoots: ["node_modules/@tsonic/dotnet-types/types"],
88
- };
89
- }
90
-
91
- return JSON.stringify(config, null, 2) + "\n";
92
- };
93
-
94
- /**
95
- * Create or update package.json with scripts and metadata
96
- */
97
- const createOrUpdatePackageJson = (packageJsonPath: string): void => {
98
- let packageJson: Record<string, unknown>;
99
-
100
- if (existsSync(packageJsonPath)) {
101
- // Merge with existing package.json
102
- const existing = readFileSync(packageJsonPath, "utf-8");
103
- packageJson = JSON.parse(existing);
104
-
105
- // Ensure required fields exist
106
- if (!packageJson.name) {
107
- packageJson.name = "my-tsonic-app";
108
- }
109
- if (!packageJson.version) {
110
- packageJson.version = "1.0.0";
111
- }
112
- if (!packageJson.type) {
113
- packageJson.type = "module";
114
- }
115
-
116
- // Merge scripts
117
- const existingScripts =
118
- (packageJson.scripts as Record<string, string>) || {};
119
- packageJson.scripts = {
120
- ...existingScripts,
121
- build: "tsonic build src/main.ts",
122
- dev: "tsonic run src/main.ts",
123
- };
124
-
125
- // Ensure devDependencies exists
126
- if (!packageJson.devDependencies) {
127
- packageJson.devDependencies = {};
128
- }
129
- } else {
130
- // Create new package.json
131
- packageJson = {
132
- name: "my-tsonic-app",
133
- version: "1.0.0",
134
- type: "module",
135
- scripts: {
136
- build: "tsonic build src/main.ts",
137
- dev: "tsonic run src/main.ts",
138
- },
139
- devDependencies: {},
140
- };
141
- }
142
-
143
- writeFileSync(
144
- packageJsonPath,
145
- JSON.stringify(packageJson, null, 2) + "\n",
146
- "utf-8"
147
- );
148
- };
149
-
150
- /**
151
- * Install npm package
152
- */
153
- const installPackage = (
154
- packageName: string,
155
- version: string
156
- ): Result<void, string> => {
157
- const result = spawnSync(
158
- "npm",
159
- ["install", "--save-dev", `${packageName}@${version}`],
160
- {
161
- stdio: "inherit",
162
- encoding: "utf-8",
163
- }
164
- );
165
-
166
- if (result.status !== 0) {
167
- return {
168
- ok: false,
169
- error: `Failed to install ${packageName}@${version}`,
170
- };
171
- }
172
-
173
- return { ok: true, value: undefined };
174
- };
175
-
176
- /**
177
- * Initialize a new Tsonic project
178
- */
179
- export const initProject = (
180
- cwd: string,
181
- options: InitOptions = {}
182
- ): Result<void, string> => {
183
- const tsonicJsonPath = join(cwd, "tsonic.json");
184
- const gitignorePath = join(cwd, ".gitignore");
185
- const srcDir = join(cwd, "src");
186
- const mainTsPath = join(srcDir, "main.ts");
187
- const readmePath = join(cwd, "README.md");
188
- const packageJsonPath = join(cwd, "package.json");
189
-
190
- // Check if tsonic.json already exists
191
- if (existsSync(tsonicJsonPath)) {
192
- return {
193
- ok: false,
194
- error: "tsonic.json already exists. Project is already initialized.",
195
- };
196
- }
197
-
198
- try {
199
- // Create or update package.json FIRST (before npm install)
200
- const packageJsonExists = existsSync(packageJsonPath);
201
- createOrUpdatePackageJson(packageJsonPath);
202
- console.log(
203
- packageJsonExists ? "✓ Updated package.json" : "✓ Created package.json"
204
- );
205
-
206
- // Install .NET type declarations
207
- const shouldInstallTypes = !options.skipTypes;
208
- const typesVersion = options.typesVersion ?? "10.0.0";
209
-
210
- if (shouldInstallTypes) {
211
- console.log(
212
- `Installing .NET type declarations (@tsonic/dotnet-types@${typesVersion})...`
213
- );
214
- const installResult = installPackage(
215
- "@tsonic/dotnet-types",
216
- typesVersion
217
- );
218
- if (!installResult.ok) {
219
- return installResult;
220
- }
221
- console.log("✓ Installed @tsonic/dotnet-types");
222
- }
223
-
224
- // Create tsonic.json
225
- const config = generateConfig(shouldInstallTypes);
226
- writeFileSync(tsonicJsonPath, config, "utf-8");
227
- console.log("✓ Created tsonic.json");
228
-
229
- // Create or append to .gitignore
230
- if (existsSync(gitignorePath)) {
231
- const existing = readFileSync(gitignorePath, "utf-8");
232
- if (!existing.includes("generated/")) {
233
- writeFileSync(
234
- gitignorePath,
235
- existing + "\n" + DEFAULT_GITIGNORE,
236
- "utf-8"
237
- );
238
- console.log("✓ Updated .gitignore");
239
- }
240
- } else {
241
- writeFileSync(gitignorePath, DEFAULT_GITIGNORE, "utf-8");
242
- console.log("✓ Created .gitignore");
243
- }
244
-
245
- // Create src directory and main.ts
246
- if (!existsSync(srcDir)) {
247
- mkdirSync(srcDir, { recursive: true });
248
- }
249
- if (!existsSync(mainTsPath)) {
250
- writeFileSync(mainTsPath, SAMPLE_MAIN_TS, "utf-8");
251
- console.log("✓ Created src/main.ts");
252
- }
253
-
254
- // Create README.md
255
- if (!existsSync(readmePath)) {
256
- writeFileSync(readmePath, SAMPLE_README, "utf-8");
257
- console.log("✓ Created README.md");
258
- }
259
-
260
- console.log("\n✓ Project initialized successfully!");
261
- console.log("\nNext steps:");
262
- console.log(" npm run build # Build executable");
263
- console.log(" npm run dev # Run directly");
264
-
265
- return { ok: true, value: undefined };
266
- } catch (error) {
267
- return {
268
- ok: false,
269
- error: `Failed to initialize project: ${error instanceof Error ? error.message : String(error)}`,
270
- };
271
- }
272
- };
@@ -1,51 +0,0 @@
1
- /**
2
- * tsonic run command - Build and execute
3
- */
4
-
5
- import { spawnSync } from "node:child_process";
6
- import type { ResolvedConfig, Result } from "../types.js";
7
- import { buildCommand } from "./build.js";
8
-
9
- /**
10
- * Build and run the executable
11
- */
12
- export const runCommand = (
13
- config: ResolvedConfig,
14
- programArgs: string[] = []
15
- ): Result<{ exitCode: number }, string> => {
16
- // Build the executable
17
- const buildResult = buildCommand(config);
18
- if (!buildResult.ok) {
19
- return buildResult;
20
- }
21
-
22
- const { outputPath } = buildResult.value;
23
-
24
- if (!config.quiet) {
25
- console.log(`\nRunning ${outputPath}...`);
26
- console.log("─".repeat(50));
27
- }
28
-
29
- // Execute the binary
30
- const runResult = spawnSync(outputPath, programArgs, {
31
- stdio: "inherit",
32
- encoding: "utf-8",
33
- });
34
-
35
- if (runResult.error) {
36
- return {
37
- ok: false,
38
- error: `Failed to run executable: ${runResult.error.message}`,
39
- };
40
- }
41
-
42
- if (!config.quiet) {
43
- console.log("─".repeat(50));
44
- console.log(`\nProcess exited with code ${runResult.status ?? 0}`);
45
- }
46
-
47
- return {
48
- ok: true,
49
- value: { exitCode: runResult.status ?? 0 },
50
- };
51
- };