@tsonic/cli 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.
@@ -1,337 +0,0 @@
1
- /**
2
- * Tests for configuration loading and resolution
3
- */
4
-
5
- import { describe, it } from "mocha";
6
- import { expect } from "chai";
7
- import { resolveConfig } from "./config.js";
8
- import type { TsonicConfig, CliOptions } from "./types.js";
9
-
10
- describe("Config", () => {
11
- describe("resolveConfig", () => {
12
- it("should use config values as defaults", () => {
13
- const config: TsonicConfig = {
14
- rootNamespace: "MyApp",
15
- entryPoint: "src/main.ts",
16
- sourceRoot: "src",
17
- outputDirectory: "dist",
18
- outputName: "myapp",
19
- rid: "linux-x64",
20
- dotnetVersion: "net10.0",
21
- optimize: "speed",
22
- };
23
-
24
- const result = resolveConfig(config, {});
25
- expect(result.rootNamespace).to.equal("MyApp");
26
- expect(result.entryPoint).to.equal("src/main.ts");
27
- expect(result.sourceRoot).to.equal("src");
28
- expect(result.outputDirectory).to.equal("dist");
29
- expect(result.outputName).to.equal("myapp");
30
- expect(result.rid).to.equal("linux-x64");
31
- expect(result.dotnetVersion).to.equal("net10.0");
32
- expect(result.optimize).to.equal("speed");
33
- });
34
-
35
- it("should override config with CLI options", () => {
36
- const config: TsonicConfig = {
37
- rootNamespace: "MyApp",
38
- outputName: "myapp",
39
- };
40
-
41
- const cliOptions: CliOptions = {
42
- namespace: "OverriddenApp",
43
- out: "custom",
44
- src: "source",
45
- optimize: "size",
46
- };
47
-
48
- const result = resolveConfig(config, cliOptions);
49
- expect(result.rootNamespace).to.equal("OverriddenApp");
50
- expect(result.outputName).to.equal("custom");
51
- expect(result.sourceRoot).to.equal("source");
52
- expect(result.optimize).to.equal("size");
53
- });
54
-
55
- it("should use entry file parameter over config", () => {
56
- const config: TsonicConfig = {
57
- rootNamespace: "MyApp",
58
- entryPoint: "src/main.ts",
59
- };
60
-
61
- const result = resolveConfig(config, {}, "custom/entry.ts");
62
- expect(result.entryPoint).to.equal("custom/entry.ts");
63
- });
64
-
65
- it("should leave entryPoint as undefined when not specified", () => {
66
- const config: TsonicConfig = {
67
- rootNamespace: "MyApp",
68
- };
69
-
70
- const result = resolveConfig(config, {});
71
- expect(result.entryPoint).to.be.undefined;
72
- });
73
-
74
- it("should default sourceRoot to dirname of entryPoint", () => {
75
- const config: TsonicConfig = {
76
- rootNamespace: "MyApp",
77
- entryPoint: "app/index.ts",
78
- };
79
-
80
- const result = resolveConfig(config, {});
81
- expect(result.sourceRoot).to.equal("app");
82
- });
83
-
84
- it("should default outputDirectory to 'generated'", () => {
85
- const config: TsonicConfig = {
86
- rootNamespace: "MyApp",
87
- };
88
-
89
- const result = resolveConfig(config, {});
90
- expect(result.outputDirectory).to.equal("generated");
91
- });
92
-
93
- it("should default outputName to 'app'", () => {
94
- const config: TsonicConfig = {
95
- rootNamespace: "MyApp",
96
- };
97
-
98
- const result = resolveConfig(config, {});
99
- expect(result.outputName).to.equal("app");
100
- });
101
-
102
- it("should default dotnetVersion to 'net10.0'", () => {
103
- const config: TsonicConfig = {
104
- rootNamespace: "MyApp",
105
- };
106
-
107
- const result = resolveConfig(config, {});
108
- expect(result.dotnetVersion).to.equal("net10.0");
109
- });
110
-
111
- it("should default optimize to 'speed'", () => {
112
- const config: TsonicConfig = {
113
- rootNamespace: "MyApp",
114
- };
115
-
116
- const result = resolveConfig(config, {});
117
- expect(result.optimize).to.equal("speed");
118
- });
119
-
120
- it("should include runtime packages by default", () => {
121
- const config: TsonicConfig = {
122
- rootNamespace: "MyApp",
123
- };
124
-
125
- const result = resolveConfig(config, {});
126
- // Default runtime is "js", so includes both Tsonic.Runtime and Tsonic.JSRuntime
127
- expect(result.packages).to.deep.equal([
128
- { name: "Tsonic.Runtime", version: "0.0.1" },
129
- { name: "Tsonic.JSRuntime", version: "0.0.1" },
130
- ]);
131
- });
132
-
133
- it("should include packages from config after runtime packages", () => {
134
- const config: TsonicConfig = {
135
- rootNamespace: "MyApp",
136
- packages: [
137
- { name: "System.Text.Json", version: "8.0.0" },
138
- { name: "Newtonsoft.Json", version: "13.0.3" },
139
- ],
140
- };
141
-
142
- const result = resolveConfig(config, {});
143
- // Runtime packages come first, then user packages
144
- expect(result.packages).to.deep.equal([
145
- { name: "Tsonic.Runtime", version: "0.0.1" },
146
- { name: "Tsonic.JSRuntime", version: "0.0.1" },
147
- { name: "System.Text.Json", version: "8.0.0" },
148
- { name: "Newtonsoft.Json", version: "13.0.3" },
149
- ]);
150
- });
151
-
152
- it("should default stripSymbols to true", () => {
153
- const config: TsonicConfig = {
154
- rootNamespace: "MyApp",
155
- };
156
-
157
- const result = resolveConfig(config, {});
158
- expect(result.stripSymbols).to.equal(true);
159
- });
160
-
161
- it("should use buildOptions.stripSymbols from config", () => {
162
- const config: TsonicConfig = {
163
- rootNamespace: "MyApp",
164
- buildOptions: {
165
- stripSymbols: false,
166
- },
167
- };
168
-
169
- const result = resolveConfig(config, {});
170
- expect(result.stripSymbols).to.equal(false);
171
- });
172
-
173
- it("should override stripSymbols with --no-strip CLI option", () => {
174
- const config: TsonicConfig = {
175
- rootNamespace: "MyApp",
176
- buildOptions: {
177
- stripSymbols: true,
178
- },
179
- };
180
-
181
- const result = resolveConfig(config, { noStrip: true });
182
- expect(result.stripSymbols).to.equal(false);
183
- });
184
-
185
- it("should default invariantGlobalization to true", () => {
186
- const config: TsonicConfig = {
187
- rootNamespace: "MyApp",
188
- };
189
-
190
- const result = resolveConfig(config, {});
191
- expect(result.invariantGlobalization).to.equal(true);
192
- });
193
-
194
- it("should use buildOptions.invariantGlobalization from config", () => {
195
- const config: TsonicConfig = {
196
- rootNamespace: "MyApp",
197
- buildOptions: {
198
- invariantGlobalization: false,
199
- },
200
- };
201
-
202
- const result = resolveConfig(config, {});
203
- expect(result.invariantGlobalization).to.equal(false);
204
- });
205
-
206
- it("should default keepTemp to false", () => {
207
- const config: TsonicConfig = {
208
- rootNamespace: "MyApp",
209
- };
210
-
211
- const result = resolveConfig(config, {});
212
- expect(result.keepTemp).to.equal(false);
213
- });
214
-
215
- it("should set keepTemp from CLI option", () => {
216
- const config: TsonicConfig = {
217
- rootNamespace: "MyApp",
218
- };
219
-
220
- const result = resolveConfig(config, { keepTemp: true });
221
- expect(result.keepTemp).to.equal(true);
222
- });
223
-
224
- it("should default verbose to false", () => {
225
- const config: TsonicConfig = {
226
- rootNamespace: "MyApp",
227
- };
228
-
229
- const result = resolveConfig(config, {});
230
- expect(result.verbose).to.equal(false);
231
- });
232
-
233
- it("should set verbose from CLI option", () => {
234
- const config: TsonicConfig = {
235
- rootNamespace: "MyApp",
236
- };
237
-
238
- const result = resolveConfig(config, { verbose: true });
239
- expect(result.verbose).to.equal(true);
240
- });
241
-
242
- it("should default quiet to false", () => {
243
- const config: TsonicConfig = {
244
- rootNamespace: "MyApp",
245
- };
246
-
247
- const result = resolveConfig(config, {});
248
- expect(result.quiet).to.equal(false);
249
- });
250
-
251
- it("should set quiet from CLI option", () => {
252
- const config: TsonicConfig = {
253
- rootNamespace: "MyApp",
254
- };
255
-
256
- const result = resolveConfig(config, { quiet: true });
257
- expect(result.quiet).to.equal(true);
258
- });
259
-
260
- it("should default typeRoots to node_modules/@tsonic/dotnet-types/types", () => {
261
- const config: TsonicConfig = {
262
- rootNamespace: "MyApp",
263
- };
264
-
265
- const result = resolveConfig(config, {});
266
- expect(result.typeRoots).to.deep.equal([
267
- "node_modules/@tsonic/dotnet-types/types",
268
- ]);
269
- });
270
-
271
- it("should use typeRoots from config.dotnet.typeRoots", () => {
272
- const config: TsonicConfig = {
273
- rootNamespace: "MyApp",
274
- dotnet: {
275
- typeRoots: ["custom/path/types", "another/path/types"],
276
- },
277
- };
278
-
279
- const result = resolveConfig(config, {});
280
- expect(result.typeRoots).to.deep.equal([
281
- "custom/path/types",
282
- "another/path/types",
283
- ]);
284
- });
285
-
286
- it("should handle all options together", () => {
287
- const config: TsonicConfig = {
288
- rootNamespace: "MyApp",
289
- entryPoint: "src/index.ts",
290
- sourceRoot: "src",
291
- outputDirectory: "out",
292
- outputName: "program",
293
- rid: "linux-x64",
294
- dotnetVersion: "net9.0",
295
- optimize: "size",
296
- packages: [{ name: "Package.Name", version: "1.0.0" }],
297
- buildOptions: {
298
- stripSymbols: false,
299
- invariantGlobalization: false,
300
- },
301
- };
302
-
303
- const cliOptions: CliOptions = {
304
- namespace: "CLI.Override",
305
- src: "source",
306
- out: "binary",
307
- rid: "win-x64",
308
- optimize: "speed",
309
- noStrip: true,
310
- keepTemp: true,
311
- verbose: true,
312
- quiet: false,
313
- };
314
-
315
- const result = resolveConfig(config, cliOptions, "custom.ts");
316
-
317
- expect(result.rootNamespace).to.equal("CLI.Override");
318
- expect(result.entryPoint).to.equal("custom.ts");
319
- expect(result.sourceRoot).to.equal("source");
320
- expect(result.outputDirectory).to.equal("out");
321
- expect(result.outputName).to.equal("binary");
322
- expect(result.rid).to.equal("win-x64");
323
- expect(result.dotnetVersion).to.equal("net9.0");
324
- expect(result.optimize).to.equal("speed");
325
- expect(result.packages).to.deep.equal([
326
- { name: "Tsonic.Runtime", version: "0.0.1" },
327
- { name: "Tsonic.JSRuntime", version: "0.0.1" },
328
- { name: "Package.Name", version: "1.0.0" },
329
- ]);
330
- expect(result.stripSymbols).to.equal(false);
331
- expect(result.invariantGlobalization).to.equal(false);
332
- expect(result.keepTemp).to.equal(true);
333
- expect(result.verbose).to.equal(true);
334
- expect(result.quiet).to.equal(false);
335
- });
336
- });
337
- });
package/src/config.ts DELETED
@@ -1,205 +0,0 @@
1
- /**
2
- * Configuration loading and validation
3
- */
4
-
5
- import { readFileSync, existsSync } from "node:fs";
6
- import { join, resolve, dirname } from "node:path";
7
- import { detectRid } from "@tsonic/backend";
8
- import type {
9
- TsonicConfig,
10
- CliOptions,
11
- ResolvedConfig,
12
- Result,
13
- TsonicOutputConfig,
14
- } from "./types.js";
15
- import type { OutputType } from "@tsonic/backend";
16
-
17
- /**
18
- * Load tsonic.json from a directory
19
- */
20
- export const loadConfig = (
21
- configPath: string
22
- ): Result<TsonicConfig, string> => {
23
- if (!existsSync(configPath)) {
24
- return {
25
- ok: false,
26
- error: `Config file not found: ${configPath}`,
27
- };
28
- }
29
-
30
- try {
31
- const content = readFileSync(configPath, "utf-8");
32
- const config = JSON.parse(content) as TsonicConfig;
33
-
34
- // Validate required fields
35
- if (!config.rootNamespace) {
36
- return {
37
- ok: false,
38
- error: "tsonic.json: 'rootNamespace' is required",
39
- };
40
- }
41
-
42
- return { ok: true, value: config };
43
- } catch (error) {
44
- return {
45
- ok: false,
46
- error: `Failed to parse tsonic.json: ${error instanceof Error ? error.message : String(error)}`,
47
- };
48
- }
49
- };
50
-
51
- /**
52
- * Find tsonic.json by walking up the directory tree
53
- */
54
- export const findConfig = (startDir: string): string | null => {
55
- let currentDir = resolve(startDir);
56
-
57
- // Walk up until we find tsonic.json or hit root
58
- while (true) {
59
- const configPath = join(currentDir, "tsonic.json");
60
- if (existsSync(configPath)) {
61
- return configPath;
62
- }
63
-
64
- const parentDir = dirname(currentDir);
65
- if (parentDir === currentDir) {
66
- // Hit root
67
- return null;
68
- }
69
- currentDir = parentDir;
70
- }
71
- };
72
-
73
- /**
74
- * Auto-detect output type based on entry point and project structure
75
- */
76
- const autoDetectOutputType = (entryPoint: string | undefined): OutputType => {
77
- // If no entry point, default to library
78
- if (!entryPoint) {
79
- return "library";
80
- }
81
-
82
- // If entry point is provided, default to executable
83
- // The user specified an entry point, so they want a runnable program
84
- return "executable";
85
- };
86
-
87
- /**
88
- * Resolve output configuration from config + CLI options
89
- */
90
- const resolveOutputConfig = (
91
- config: TsonicConfig,
92
- cliOptions: CliOptions,
93
- entryPoint: string | undefined
94
- ): TsonicOutputConfig => {
95
- const configOutput = config.output ?? {};
96
-
97
- // Determine output type
98
- const outputType =
99
- cliOptions.type ?? configOutput.type ?? autoDetectOutputType(entryPoint);
100
-
101
- // Base config from file
102
- const baseConfig: TsonicOutputConfig = {
103
- type: outputType,
104
- name: configOutput.name ?? config.outputName,
105
- };
106
-
107
- // Merge executable-specific options
108
- if (outputType === "executable") {
109
- return {
110
- ...baseConfig,
111
- nativeAot: cliOptions.noAot ? false : (configOutput.nativeAot ?? true),
112
- singleFile: cliOptions.singleFile ?? configOutput.singleFile ?? true,
113
- trimmed: configOutput.trimmed ?? true,
114
- stripSymbols: cliOptions.noStrip
115
- ? false
116
- : (configOutput.stripSymbols ?? true),
117
- optimization: cliOptions.optimize ?? configOutput.optimization ?? "speed",
118
- invariantGlobalization:
119
- config.buildOptions?.invariantGlobalization ?? true,
120
- selfContained:
121
- cliOptions.selfContained ?? configOutput.selfContained ?? true,
122
- };
123
- }
124
-
125
- // Merge library-specific options
126
- if (outputType === "library") {
127
- return {
128
- ...baseConfig,
129
- targetFrameworks: configOutput.targetFrameworks ?? [
130
- config.dotnetVersion ?? "net10.0",
131
- ],
132
- generateDocumentation:
133
- cliOptions.generateDocs ?? configOutput.generateDocumentation ?? true,
134
- includeSymbols:
135
- cliOptions.includeSymbols ?? configOutput.includeSymbols ?? true,
136
- packable: cliOptions.pack ?? configOutput.packable ?? false,
137
- package: configOutput.package,
138
- };
139
- }
140
-
141
- // Console app fallback
142
- return {
143
- ...baseConfig,
144
- singleFile: cliOptions.singleFile ?? configOutput.singleFile ?? true,
145
- selfContained:
146
- cliOptions.selfContained ?? configOutput.selfContained ?? true,
147
- };
148
- };
149
-
150
- /**
151
- * Resolve final configuration from file + CLI args
152
- */
153
- export const resolveConfig = (
154
- config: TsonicConfig,
155
- cliOptions: CliOptions,
156
- entryFile?: string
157
- ): ResolvedConfig => {
158
- const entryPoint = entryFile ?? config.entryPoint;
159
- const sourceRoot =
160
- cliOptions.src ??
161
- config.sourceRoot ??
162
- (entryPoint ? dirname(entryPoint) : "src");
163
-
164
- // Default type roots based on runtime mode
165
- // Only ambient globals packages need typeRoots - explicit import packages are resolved normally
166
- const runtime = config.runtime ?? "js";
167
- const defaultTypeRoots =
168
- runtime === "js"
169
- ? ["node_modules/@tsonic/js-globals"]
170
- : ["node_modules/@tsonic/dotnet-globals"];
171
- const typeRoots = config.dotnet?.typeRoots ?? defaultTypeRoots;
172
-
173
- // Merge libraries from config and CLI
174
- const configLibraries = config.dotnet?.libraries ?? [];
175
- const cliLibraries = cliOptions.lib ?? [];
176
- const libraries = [...configLibraries, ...cliLibraries];
177
-
178
- // Resolve output configuration
179
- const outputConfig = resolveOutputConfig(config, cliOptions, entryPoint);
180
-
181
- return {
182
- rootNamespace: cliOptions.namespace ?? config.rootNamespace,
183
- entryPoint,
184
- sourceRoot,
185
- outputDirectory: config.outputDirectory ?? "generated",
186
- outputName: cliOptions.out ?? config.outputName ?? "app",
187
- rid: cliOptions.rid ?? config.rid ?? detectRid(),
188
- dotnetVersion: config.dotnetVersion ?? "net10.0",
189
- optimize: cliOptions.optimize ?? config.optimize ?? "speed",
190
- runtime: config.runtime ?? "js",
191
- // Only include user-specified packages
192
- // Runtime DLLs are bundled with @tsonic/tsonic and added as assembly references
193
- packages: config.dotnet?.packages ?? config.packages ?? [],
194
- outputConfig,
195
- stripSymbols: cliOptions.noStrip
196
- ? false
197
- : (config.buildOptions?.stripSymbols ?? true),
198
- invariantGlobalization: config.buildOptions?.invariantGlobalization ?? true,
199
- keepTemp: cliOptions.keepTemp ?? false,
200
- verbose: cliOptions.verbose ?? false,
201
- quiet: cliOptions.quiet ?? false,
202
- typeRoots,
203
- libraries,
204
- };
205
- };
package/src/index.ts DELETED
@@ -1,23 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Tsonic CLI - Command-line interface for Tsonic compiler
4
- */
5
-
6
- import { runCli } from "./cli.js";
7
-
8
- // Run CLI with arguments (skip node and script name)
9
- const args = process.argv.slice(2);
10
-
11
- runCli(args)
12
- .then((exitCode) => {
13
- process.exit(exitCode);
14
- })
15
- .catch((error) => {
16
- console.error("Fatal error:", error);
17
- process.exit(1);
18
- });
19
-
20
- // Export for testing
21
- export { runCli } from "./cli.js";
22
- export * from "./types.js";
23
- export * from "./config.js";
package/src/types.ts DELETED
@@ -1,121 +0,0 @@
1
- /**
2
- * Type definitions for CLI
3
- */
4
-
5
- import type {
6
- NuGetPackage,
7
- OutputType,
8
- PackageMetadata,
9
- } from "@tsonic/backend";
10
-
11
- /**
12
- * Output configuration in tsonic.json
13
- */
14
- export type TsonicOutputConfig = {
15
- readonly type?: OutputType;
16
- readonly name?: string;
17
- // Executable options
18
- readonly nativeAot?: boolean;
19
- readonly singleFile?: boolean;
20
- readonly trimmed?: boolean;
21
- readonly stripSymbols?: boolean;
22
- readonly optimization?: "size" | "speed";
23
- readonly invariantGlobalization?: boolean;
24
- readonly selfContained?: boolean;
25
- // Library options
26
- readonly targetFrameworks?: readonly string[];
27
- readonly generateDocumentation?: boolean;
28
- readonly includeSymbols?: boolean;
29
- readonly packable?: boolean;
30
- readonly package?: PackageMetadata;
31
- };
32
-
33
- /**
34
- * Tsonic configuration file (tsonic.json)
35
- */
36
- export type TsonicConfig = {
37
- readonly $schema?: string;
38
- readonly rootNamespace: string;
39
- readonly entryPoint?: string;
40
- readonly sourceRoot?: string;
41
- readonly outputDirectory?: string;
42
- readonly outputName?: string;
43
- readonly rid?: string;
44
- readonly dotnetVersion?: string;
45
- readonly optimize?: "size" | "speed";
46
- readonly runtime?: "js" | "dotnet"; // Runtime mode
47
- readonly output?: TsonicOutputConfig;
48
- readonly packages?: readonly NuGetPackage[]; // Deprecated - use dotnet.packages
49
- readonly buildOptions?: {
50
- readonly stripSymbols?: boolean;
51
- readonly invariantGlobalization?: boolean;
52
- };
53
- readonly dotnet?: {
54
- readonly typeRoots?: readonly string[];
55
- readonly packages?: readonly NuGetPackage[];
56
- readonly libraries?: readonly string[]; // External library paths for .NET interop
57
- };
58
- };
59
-
60
- /**
61
- * CLI command options (mutable for parsing)
62
- */
63
- export type CliOptions = {
64
- verbose?: boolean;
65
- quiet?: boolean;
66
- config?: string;
67
- src?: string;
68
- out?: string;
69
- namespace?: string;
70
- rid?: string;
71
- runtime?: "js" | "dotnet"; // Runtime mode
72
- optimize?: "size" | "speed";
73
- keepTemp?: boolean;
74
- noStrip?: boolean;
75
- packages?: string;
76
- lib?: string[]; // External library paths for .NET interop
77
- // Project init options
78
- skipTypes?: boolean;
79
- typesVersion?: string;
80
- // Output type options
81
- type?: OutputType;
82
- targetFramework?: string;
83
- noAot?: boolean;
84
- singleFile?: boolean;
85
- selfContained?: boolean;
86
- // Library options
87
- generateDocs?: boolean;
88
- includeSymbols?: boolean;
89
- pack?: boolean;
90
- };
91
-
92
- /**
93
- * Combined configuration (from file + CLI args)
94
- */
95
- export type ResolvedConfig = {
96
- readonly rootNamespace: string;
97
- readonly entryPoint: string | undefined;
98
- readonly sourceRoot: string;
99
- readonly outputDirectory: string;
100
- readonly outputName: string;
101
- readonly rid: string;
102
- readonly dotnetVersion: string;
103
- readonly optimize: "size" | "speed";
104
- readonly runtime: "js" | "dotnet";
105
- readonly packages: readonly NuGetPackage[];
106
- readonly outputConfig: TsonicOutputConfig;
107
- readonly stripSymbols: boolean;
108
- readonly invariantGlobalization: boolean;
109
- readonly keepTemp: boolean;
110
- readonly verbose: boolean;
111
- readonly quiet: boolean;
112
- readonly typeRoots: readonly string[];
113
- readonly libraries: readonly string[]; // External library paths for .NET interop
114
- };
115
-
116
- /**
117
- * Result type for operations
118
- */
119
- export type Result<T, E> =
120
- | { readonly ok: true; readonly value: T }
121
- | { readonly ok: false; readonly error: E };
package/tsconfig.json DELETED
@@ -1,18 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "outDir": "./dist",
5
- "rootDir": "./src",
6
- "composite": true,
7
- "declaration": true,
8
- "declarationMap": true,
9
- "tsBuildInfoFile": "./dist/.tsbuildinfo"
10
- },
11
- "include": ["src/**/*.ts"],
12
- "exclude": ["node_modules", "dist"],
13
- "references": [
14
- { "path": "../frontend" },
15
- { "path": "../emitter" },
16
- { "path": "../backend" }
17
- ]
18
- }