@tsonic/cli 0.0.1 → 0.0.3

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.
@@ -1,334 +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
- readonly runtime?: "js" | "dotnet";
14
- };
15
-
16
- const DEFAULT_GITIGNORE = `# .NET build artifacts
17
- generated/bin/
18
- generated/obj/
19
-
20
- # Optional: Uncomment to ignore generated C# files
21
- # generated/**/*.cs
22
-
23
- # Output executables
24
- *.exe
25
- app
26
-
27
- # Dependencies
28
- node_modules/
29
- `;
30
-
31
- const SAMPLE_MAIN_TS_JS = `export function main(): void {
32
- console.log("Hello from Tsonic!");
33
-
34
- const numbers = [1, 2, 3, 4, 5];
35
- const doubled = numbers.map((n) => n * 2);
36
- console.log("Doubled:", doubled.join(", "));
37
- }
38
- `;
39
-
40
- const SAMPLE_MAIN_TS_DOTNET = `import { Console } from "@tsonic/dotnet/System";
41
- import { File } from "@tsonic/dotnet/System.IO";
42
-
43
- export function main(): void {
44
- Console.WriteLine("Reading README.md...");
45
- const content = File.ReadAllText("./README.md");
46
- Console.WriteLine(content);
47
- }
48
- `;
49
-
50
- const SAMPLE_README = `# My Tsonic Project
51
-
52
- This is a sample Tsonic project that demonstrates .NET interop.
53
-
54
- ## Getting Started
55
-
56
- Build and run the project:
57
-
58
- \`\`\`bash
59
- npm run build
60
- ./app
61
- \`\`\`
62
-
63
- Or run directly:
64
-
65
- \`\`\`bash
66
- npm run dev
67
- \`\`\`
68
-
69
- ## Project Structure
70
-
71
- - \`src/main.ts\` - Entry point
72
- - \`tsonic.json\` - Project configuration
73
- - \`generated/\` - Generated C# code (gitignored)
74
- `;
75
-
76
- type TypePackageInfo = {
77
- readonly packages: readonly { name: string; version: string }[];
78
- readonly typeRoots: readonly string[];
79
- };
80
-
81
- // CLI package version - installed as devDependency for npm run build/dev
82
- const CLI_PACKAGE = { name: "@tsonic/cli", version: "0.0.1" };
83
-
84
- /**
85
- * Get type package info based on runtime mode
86
- *
87
- * typeRoots: Only ambient globals packages (provide global types without imports)
88
- * packages: All type packages to install (includes explicit import packages)
89
- */
90
- const getTypePackageInfo = (runtime: "js" | "dotnet"): TypePackageInfo => {
91
- if (runtime === "js") {
92
- // JS mode:
93
- // - @tsonic/cli: the compiler CLI (provides `tsonic` command)
94
- // - @tsonic/js-globals: ambient globals (Array, console, etc.) - needs typeRoots
95
- // - @tsonic/types: explicit imports (int, float, etc.) - just npm dep
96
- return {
97
- packages: [
98
- CLI_PACKAGE,
99
- { name: "@tsonic/js-globals", version: "0.1.1" },
100
- { name: "@tsonic/types", version: "0.2.0" },
101
- ],
102
- typeRoots: ["node_modules/@tsonic/js-globals"],
103
- };
104
- }
105
- // Dotnet mode:
106
- // - @tsonic/cli: the compiler CLI (provides `tsonic` command)
107
- // - @tsonic/dotnet-globals: ambient globals - needs typeRoots
108
- // - @tsonic/dotnet: explicit imports (System.*, etc.) - just npm dep
109
- // - @tsonic/types: transitive dep of @tsonic/dotnet
110
- return {
111
- packages: [
112
- CLI_PACKAGE,
113
- { name: "@tsonic/dotnet-globals", version: "0.1.2" },
114
- { name: "@tsonic/dotnet", version: "0.4.0" },
115
- ],
116
- typeRoots: ["node_modules/@tsonic/dotnet-globals"],
117
- };
118
- };
119
-
120
- /**
121
- * Generate tsonic.json config
122
- */
123
- const generateConfig = (
124
- includeTypeRoots: boolean,
125
- runtime: "js" | "dotnet"
126
- ): string => {
127
- const config: Record<string, unknown> = {
128
- $schema: "https://tsonic.dev/schema/v1.json",
129
- rootNamespace: "MyApp",
130
- entryPoint: "src/app.ts",
131
- sourceRoot: "src",
132
- outputDirectory: "generated",
133
- outputName: "app",
134
- runtime: runtime,
135
- optimize: "speed",
136
- packages: [],
137
- buildOptions: {
138
- stripSymbols: true,
139
- invariantGlobalization: true,
140
- },
141
- };
142
-
143
- if (includeTypeRoots) {
144
- const typeInfo = getTypePackageInfo(runtime);
145
- config.dotnet = {
146
- typeRoots: typeInfo.typeRoots,
147
- };
148
- }
149
-
150
- return JSON.stringify(config, null, 2) + "\n";
151
- };
152
-
153
- /**
154
- * Create or update package.json with scripts and metadata
155
- */
156
- const createOrUpdatePackageJson = (packageJsonPath: string): void => {
157
- let packageJson: Record<string, unknown>;
158
-
159
- if (existsSync(packageJsonPath)) {
160
- // Merge with existing package.json
161
- const existing = readFileSync(packageJsonPath, "utf-8");
162
- packageJson = JSON.parse(existing);
163
-
164
- // Ensure required fields exist
165
- if (!packageJson.name) {
166
- packageJson.name = "my-tsonic-app";
167
- }
168
- if (!packageJson.version) {
169
- packageJson.version = "1.0.0";
170
- }
171
- if (!packageJson.type) {
172
- packageJson.type = "module";
173
- }
174
-
175
- // Merge scripts
176
- const existingScripts =
177
- (packageJson.scripts as Record<string, string>) || {};
178
- packageJson.scripts = {
179
- ...existingScripts,
180
- build: "tsonic build src/app.ts",
181
- dev: "tsonic run src/app.ts",
182
- };
183
-
184
- // Ensure devDependencies exists
185
- if (!packageJson.devDependencies) {
186
- packageJson.devDependencies = {};
187
- }
188
- } else {
189
- // Create new package.json
190
- packageJson = {
191
- name: "my-tsonic-app",
192
- version: "1.0.0",
193
- type: "module",
194
- scripts: {
195
- build: "tsonic build src/app.ts",
196
- dev: "tsonic run src/app.ts",
197
- },
198
- devDependencies: {},
199
- };
200
- }
201
-
202
- writeFileSync(
203
- packageJsonPath,
204
- JSON.stringify(packageJson, null, 2) + "\n",
205
- "utf-8"
206
- );
207
- };
208
-
209
- /**
210
- * Install npm package
211
- */
212
- const installPackage = (
213
- packageName: string,
214
- version: string
215
- ): Result<void, string> => {
216
- const result = spawnSync(
217
- "npm",
218
- ["install", "--save-dev", `${packageName}@${version}`],
219
- {
220
- stdio: "inherit",
221
- encoding: "utf-8",
222
- }
223
- );
224
-
225
- if (result.status !== 0) {
226
- return {
227
- ok: false,
228
- error: `Failed to install ${packageName}@${version}`,
229
- };
230
- }
231
-
232
- return { ok: true, value: undefined };
233
- };
234
-
235
- /**
236
- * Initialize a new Tsonic project
237
- */
238
- export const initProject = (
239
- cwd: string,
240
- options: InitOptions = {}
241
- ): Result<void, string> => {
242
- const runtime = options.runtime ?? "js";
243
- const tsonicJsonPath = join(cwd, "tsonic.json");
244
- const gitignorePath = join(cwd, ".gitignore");
245
- const srcDir = join(cwd, "src");
246
- const appTsPath = join(srcDir, "app.ts");
247
- const readmePath = join(cwd, "README.md");
248
- const packageJsonPath = join(cwd, "package.json");
249
-
250
- // Check if tsonic.json already exists
251
- if (existsSync(tsonicJsonPath)) {
252
- return {
253
- ok: false,
254
- error: "tsonic.json already exists. Project is already initialized.",
255
- };
256
- }
257
-
258
- try {
259
- // Create or update package.json FIRST (before npm install)
260
- const packageJsonExists = existsSync(packageJsonPath);
261
- createOrUpdatePackageJson(packageJsonPath);
262
- console.log(
263
- packageJsonExists ? "✓ Updated package.json" : "✓ Created package.json"
264
- );
265
-
266
- // Install type declarations based on runtime mode
267
- const shouldInstallTypes = !options.skipTypes;
268
- const typeInfo = getTypePackageInfo(runtime);
269
-
270
- if (shouldInstallTypes) {
271
- for (const pkg of typeInfo.packages) {
272
- const version = options.typesVersion ?? pkg.version;
273
- console.log(`Installing type declarations (${pkg.name}@${version})...`);
274
- const installResult = installPackage(pkg.name, version);
275
- if (!installResult.ok) {
276
- return installResult;
277
- }
278
- console.log(`✓ Installed ${pkg.name}`);
279
- }
280
- }
281
-
282
- // Create tsonic.json
283
- const config = generateConfig(shouldInstallTypes, runtime);
284
- writeFileSync(tsonicJsonPath, config, "utf-8");
285
- console.log("✓ Created tsonic.json");
286
-
287
- // Create or append to .gitignore
288
- if (existsSync(gitignorePath)) {
289
- const existing = readFileSync(gitignorePath, "utf-8");
290
- if (!existing.includes("generated/")) {
291
- writeFileSync(
292
- gitignorePath,
293
- existing + "\n" + DEFAULT_GITIGNORE,
294
- "utf-8"
295
- );
296
- console.log("✓ Updated .gitignore");
297
- }
298
- } else {
299
- writeFileSync(gitignorePath, DEFAULT_GITIGNORE, "utf-8");
300
- console.log("✓ Created .gitignore");
301
- }
302
-
303
- // Create src directory and app.ts with runtime-appropriate code
304
- if (!existsSync(srcDir)) {
305
- mkdirSync(srcDir, { recursive: true });
306
- }
307
- if (!existsSync(appTsPath)) {
308
- const sampleCode =
309
- runtime === "js" ? SAMPLE_MAIN_TS_JS : SAMPLE_MAIN_TS_DOTNET;
310
- writeFileSync(appTsPath, sampleCode, "utf-8");
311
- console.log("✓ Created src/app.ts");
312
- }
313
-
314
- // Create README.md
315
- if (!existsSync(readmePath)) {
316
- writeFileSync(readmePath, SAMPLE_README, "utf-8");
317
- console.log("✓ Created README.md");
318
- }
319
-
320
- // Note: .csproj is generated by the build command with proper runtime DLL references
321
-
322
- console.log("\n✓ Project initialized successfully!");
323
- console.log("\nNext steps:");
324
- console.log(" npm run build # Build executable");
325
- console.log(" npm run dev # Run directly");
326
-
327
- return { ok: true, value: undefined };
328
- } catch (error) {
329
- return {
330
- ok: false,
331
- error: `Failed to initialize project: ${error instanceof Error ? error.message : String(error)}`,
332
- };
333
- }
334
- };
@@ -1,114 +0,0 @@
1
- /**
2
- * tsonic pack command - Create NuGet package from library
3
- */
4
-
5
- import { spawnSync } from "node:child_process";
6
- import { join } from "node:path";
7
- import { existsSync } from "node:fs";
8
- import type { ResolvedConfig, Result } from "../types.js";
9
- import { emitCommand } from "./emit.js";
10
-
11
- /**
12
- * Pack library into NuGet package
13
- */
14
- export const packCommand = (
15
- config: ResolvedConfig
16
- ): Result<{ outputPath: string }, string> => {
17
- const { outputDirectory, outputName, quiet, verbose } = config;
18
-
19
- // Verify this is a library project
20
- if (config.outputConfig.type !== "library") {
21
- return {
22
- ok: false,
23
- error:
24
- "Pack command can only be used with library projects. Set output.type to 'library' in tsonic.json",
25
- };
26
- }
27
-
28
- // Verify packable is enabled
29
- if (!config.outputConfig.packable) {
30
- return {
31
- ok: false,
32
- error:
33
- "Library is not packable. Set output.packable to true in tsonic.json",
34
- };
35
- }
36
-
37
- // Step 1: Emit C# code
38
- if (!quiet) {
39
- console.log("Step 1/2: Generating C# code...");
40
- }
41
-
42
- const emitResult = emitCommand(config);
43
- if (!emitResult.ok) {
44
- return emitResult;
45
- }
46
-
47
- const generatedDir = emitResult.value.outputDir;
48
- const csprojPath = join(generatedDir, "tsonic.csproj");
49
-
50
- if (!existsSync(csprojPath)) {
51
- return {
52
- ok: false,
53
- error: `No tsonic.csproj found in ${outputDirectory}/. This should have been created by emit.`,
54
- };
55
- }
56
-
57
- // Step 2: Run dotnet pack
58
- if (!quiet) {
59
- console.log("Step 2/2: Creating NuGet package...");
60
- }
61
-
62
- const packArgs = ["pack", "tsonic.csproj", "-c", "Release", "--nologo"];
63
-
64
- if (quiet) {
65
- packArgs.push("--verbosity", "quiet");
66
- } else if (verbose) {
67
- packArgs.push("--verbosity", "detailed");
68
- } else {
69
- packArgs.push("--verbosity", "minimal");
70
- }
71
-
72
- const packResult = spawnSync("dotnet", packArgs, {
73
- cwd: generatedDir,
74
- stdio: verbose ? "inherit" : "pipe",
75
- encoding: "utf-8",
76
- });
77
-
78
- if (packResult.status !== 0) {
79
- const errorMsg = packResult.stderr || packResult.stdout || "Unknown error";
80
- return {
81
- ok: false,
82
- error: `dotnet pack failed:\n${errorMsg}`,
83
- };
84
- }
85
-
86
- // Find the generated .nupkg file
87
- const nupkgDir = join(generatedDir, "bin", "Release");
88
-
89
- // Package metadata
90
- const packageId = config.outputConfig.package?.id ?? outputName;
91
- const packageVersion = config.outputConfig.package?.version ?? "1.0.0";
92
- const nupkgName = `${packageId}.${packageVersion}.nupkg`;
93
- const nupkgPath = join(nupkgDir, nupkgName);
94
-
95
- if (!existsSync(nupkgPath)) {
96
- return {
97
- ok: false,
98
- error: `NuGet package not found at ${nupkgPath}. Check dotnet pack output for errors.`,
99
- };
100
- }
101
-
102
- if (!quiet) {
103
- console.log(`\n✓ Package created: ${nupkgPath}`);
104
- console.log(`\nTo publish to NuGet.org:`);
105
- console.log(
106
- ` dotnet nuget push ${nupkgPath} --api-key YOUR_API_KEY --source https://api.nuget.org/v3/index.json`
107
- );
108
- }
109
-
110
- return {
111
- ok: true,
112
- value: { outputPath: nupkgPath },
113
- };
114
- };
@@ -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
- };