@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.
- package/dist/.tsbuildinfo +1 -0
- package/dist/commands/emit.d.ts.map +1 -1
- package/dist/commands/emit.js +8 -7
- package/dist/commands/emit.js.map +1 -1
- package/dist/commands/init.js +1 -1
- package/package.json +8 -4
- package/runtime/Tsonic.JSRuntime.dll +0 -0
- package/runtime/Tsonic.Runtime.dll +0 -0
- package/src/cli/constants.ts +0 -5
- package/src/cli/dispatcher.ts +0 -129
- package/src/cli/help.ts +0 -56
- package/src/cli/index.ts +0 -8
- package/src/cli/parser.test.ts +0 -259
- package/src/cli/parser.ts +0 -128
- package/src/cli.ts +0 -6
- package/src/commands/build.ts +0 -264
- package/src/commands/emit.ts +0 -406
- package/src/commands/init.ts +0 -334
- package/src/commands/pack.ts +0 -114
- package/src/commands/run.ts +0 -51
- package/src/config.test.ts +0 -337
- package/src/config.ts +0 -205
- package/src/index.ts +0 -23
- package/src/types.ts +0 -121
- package/tsconfig.json +0 -18
package/src/cli/parser.test.ts
DELETED
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for CLI argument parser
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { describe, it } from "mocha";
|
|
6
|
-
import { expect } from "chai";
|
|
7
|
-
import { parseArgs } from "./parser.js";
|
|
8
|
-
|
|
9
|
-
describe("CLI Parser", () => {
|
|
10
|
-
describe("parseArgs", () => {
|
|
11
|
-
describe("Commands", () => {
|
|
12
|
-
it("should parse build command", () => {
|
|
13
|
-
const result = parseArgs(["build"]);
|
|
14
|
-
expect(result.command).to.equal("build");
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it("should parse run command", () => {
|
|
18
|
-
const result = parseArgs(["run"]);
|
|
19
|
-
expect(result.command).to.equal("run");
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("should parse emit command", () => {
|
|
23
|
-
const result = parseArgs(["emit"]);
|
|
24
|
-
expect(result.command).to.equal("emit");
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it("should parse project:init as two-word command", () => {
|
|
28
|
-
const result = parseArgs(["project", "init"]);
|
|
29
|
-
expect(result.command).to.equal("project:init");
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("should parse help command from --help", () => {
|
|
33
|
-
const result = parseArgs(["--help"]);
|
|
34
|
-
expect(result.command).to.equal("help");
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("should parse help command from -h", () => {
|
|
38
|
-
const result = parseArgs(["-h"]);
|
|
39
|
-
expect(result.command).to.equal("help");
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it("should parse version command from --version", () => {
|
|
43
|
-
const result = parseArgs(["--version"]);
|
|
44
|
-
expect(result.command).to.equal("version");
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it("should parse version command from -v", () => {
|
|
48
|
-
const result = parseArgs(["-v"]);
|
|
49
|
-
expect(result.command).to.equal("version");
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
describe("Entry File", () => {
|
|
54
|
-
it("should parse entry file after command", () => {
|
|
55
|
-
const result = parseArgs(["run", "index.ts"]);
|
|
56
|
-
expect(result.command).to.equal("run");
|
|
57
|
-
expect(result.entryFile).to.equal("index.ts");
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it("should parse entry file with path", () => {
|
|
61
|
-
const result = parseArgs(["build", "src/main.ts"]);
|
|
62
|
-
expect(result.command).to.equal("build");
|
|
63
|
-
expect(result.entryFile).to.equal("src/main.ts");
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it("should handle no entry file", () => {
|
|
67
|
-
const result = parseArgs(["build"]);
|
|
68
|
-
expect(result.entryFile).to.equal(undefined);
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
describe("Options", () => {
|
|
73
|
-
it("should parse --verbose option", () => {
|
|
74
|
-
const result = parseArgs(["build", "--verbose"]);
|
|
75
|
-
expect(result.options.verbose).to.equal(true);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it("should parse -V short option for verbose", () => {
|
|
79
|
-
const result = parseArgs(["build", "-V"]);
|
|
80
|
-
expect(result.options.verbose).to.equal(true);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it("should parse --quiet option", () => {
|
|
84
|
-
const result = parseArgs(["build", "--quiet"]);
|
|
85
|
-
expect(result.options.quiet).to.equal(true);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("should parse -q short option for quiet", () => {
|
|
89
|
-
const result = parseArgs(["build", "-q"]);
|
|
90
|
-
expect(result.options.quiet).to.equal(true);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it("should parse --config option with value", () => {
|
|
94
|
-
const result = parseArgs(["build", "--config", "tsonic.json"]);
|
|
95
|
-
expect(result.options.config).to.equal("tsonic.json");
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it("should parse -c short option for config", () => {
|
|
99
|
-
const result = parseArgs(["build", "-c", "custom.json"]);
|
|
100
|
-
expect(result.options.config).to.equal("custom.json");
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it("should parse --src option with value", () => {
|
|
104
|
-
const result = parseArgs(["build", "--src", "source"]);
|
|
105
|
-
expect(result.options.src).to.equal("source");
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it("should parse -s short option for src", () => {
|
|
109
|
-
const result = parseArgs(["build", "-s", "app"]);
|
|
110
|
-
expect(result.options.src).to.equal("app");
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it("should parse --out option with value", () => {
|
|
114
|
-
const result = parseArgs(["build", "--out", "output"]);
|
|
115
|
-
expect(result.options.out).to.equal("output");
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it("should parse -o short option for out", () => {
|
|
119
|
-
const result = parseArgs(["build", "-o", "dist"]);
|
|
120
|
-
expect(result.options.out).to.equal("dist");
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it("should parse --namespace option with value", () => {
|
|
124
|
-
const result = parseArgs(["build", "--namespace", "MyApp"]);
|
|
125
|
-
expect(result.options.namespace).to.equal("MyApp");
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it("should parse -n short option for namespace", () => {
|
|
129
|
-
const result = parseArgs(["build", "-n", "App"]);
|
|
130
|
-
expect(result.options.namespace).to.equal("App");
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it("should parse --rid option with value", () => {
|
|
134
|
-
const result = parseArgs(["build", "--rid", "linux-x64"]);
|
|
135
|
-
expect(result.options.rid).to.equal("linux-x64");
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it("should parse -r short option for rid", () => {
|
|
139
|
-
const result = parseArgs(["build", "-r", "win-x64"]);
|
|
140
|
-
expect(result.options.rid).to.equal("win-x64");
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it("should parse --optimize option with speed", () => {
|
|
144
|
-
const result = parseArgs(["build", "--optimize", "speed"]);
|
|
145
|
-
expect(result.options.optimize).to.equal("speed");
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it("should parse --optimize option with size", () => {
|
|
149
|
-
const result = parseArgs(["build", "--optimize", "size"]);
|
|
150
|
-
expect(result.options.optimize).to.equal("size");
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it("should parse -O short option for optimize", () => {
|
|
154
|
-
const result = parseArgs(["build", "-O", "speed"]);
|
|
155
|
-
expect(result.options.optimize).to.equal("speed");
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it("should parse --keep-temp option", () => {
|
|
159
|
-
const result = parseArgs(["build", "--keep-temp"]);
|
|
160
|
-
expect(result.options.keepTemp).to.equal(true);
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it("should parse -k short option for keep-temp", () => {
|
|
164
|
-
const result = parseArgs(["build", "-k"]);
|
|
165
|
-
expect(result.options.keepTemp).to.equal(true);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
it("should parse --no-strip option", () => {
|
|
169
|
-
const result = parseArgs(["build", "--no-strip"]);
|
|
170
|
-
expect(result.options.noStrip).to.equal(true);
|
|
171
|
-
});
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
describe("Program Arguments", () => {
|
|
175
|
-
it("should parse program arguments after --", () => {
|
|
176
|
-
const result = parseArgs(["run", "index.ts", "--", "arg1", "arg2"]);
|
|
177
|
-
expect(result.programArgs).to.deep.equal(["arg1", "arg2"]);
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
it("should handle options in program arguments", () => {
|
|
181
|
-
const result = parseArgs([
|
|
182
|
-
"run",
|
|
183
|
-
"index.ts",
|
|
184
|
-
"--",
|
|
185
|
-
"--verbose",
|
|
186
|
-
"-o",
|
|
187
|
-
"out",
|
|
188
|
-
]);
|
|
189
|
-
expect(result.programArgs).to.deep.equal(["--verbose", "-o", "out"]);
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it("should handle empty program arguments", () => {
|
|
193
|
-
const result = parseArgs(["run", "index.ts"]);
|
|
194
|
-
expect(result.programArgs).to.deep.equal([]);
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
describe("Complex Scenarios", () => {
|
|
199
|
-
it("should parse command with entry file and multiple options", () => {
|
|
200
|
-
const result = parseArgs([
|
|
201
|
-
"build",
|
|
202
|
-
"src/main.ts",
|
|
203
|
-
"--namespace",
|
|
204
|
-
"MyApp",
|
|
205
|
-
"--out",
|
|
206
|
-
"dist",
|
|
207
|
-
"--verbose",
|
|
208
|
-
]);
|
|
209
|
-
expect(result.command).to.equal("build");
|
|
210
|
-
expect(result.entryFile).to.equal("src/main.ts");
|
|
211
|
-
expect(result.options.namespace).to.equal("MyApp");
|
|
212
|
-
expect(result.options.out).to.equal("dist");
|
|
213
|
-
expect(result.options.verbose).to.equal(true);
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
it("should parse run command with entry, options, and program args", () => {
|
|
217
|
-
const result = parseArgs([
|
|
218
|
-
"run",
|
|
219
|
-
"index.ts",
|
|
220
|
-
"--verbose",
|
|
221
|
-
"--",
|
|
222
|
-
"programArg1",
|
|
223
|
-
"programArg2",
|
|
224
|
-
]);
|
|
225
|
-
expect(result.command).to.equal("run");
|
|
226
|
-
expect(result.entryFile).to.equal("index.ts");
|
|
227
|
-
expect(result.options.verbose).to.equal(true);
|
|
228
|
-
expect(result.programArgs).to.deep.equal([
|
|
229
|
-
"programArg1",
|
|
230
|
-
"programArg2",
|
|
231
|
-
]);
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
it("should handle empty args array", () => {
|
|
235
|
-
const result = parseArgs([]);
|
|
236
|
-
expect(result.command).to.equal("");
|
|
237
|
-
expect(result.entryFile).to.equal(undefined);
|
|
238
|
-
expect(result.options).to.deep.equal({});
|
|
239
|
-
expect(result.programArgs).to.deep.equal([]);
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
it("should handle mixed short and long options", () => {
|
|
243
|
-
const result = parseArgs([
|
|
244
|
-
"build",
|
|
245
|
-
"-n",
|
|
246
|
-
"App",
|
|
247
|
-
"--out",
|
|
248
|
-
"dist",
|
|
249
|
-
"-V",
|
|
250
|
-
"--keep-temp",
|
|
251
|
-
]);
|
|
252
|
-
expect(result.options.namespace).to.equal("App");
|
|
253
|
-
expect(result.options.out).to.equal("dist");
|
|
254
|
-
expect(result.options.verbose).to.equal(true);
|
|
255
|
-
expect(result.options.keepTemp).to.equal(true);
|
|
256
|
-
});
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
});
|
package/src/cli/parser.ts
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CLI argument parser
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { CliOptions } from "../types.js";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Parse CLI arguments
|
|
9
|
-
*/
|
|
10
|
-
export const parseArgs = (
|
|
11
|
-
args: string[]
|
|
12
|
-
): {
|
|
13
|
-
command: string;
|
|
14
|
-
entryFile?: string;
|
|
15
|
-
options: CliOptions;
|
|
16
|
-
programArgs?: string[];
|
|
17
|
-
} => {
|
|
18
|
-
const options: CliOptions = {};
|
|
19
|
-
let command = "";
|
|
20
|
-
let entryFile: string | undefined;
|
|
21
|
-
const programArgs: string[] = [];
|
|
22
|
-
let captureProgramArgs = false;
|
|
23
|
-
|
|
24
|
-
for (let i = 0; i < args.length; i++) {
|
|
25
|
-
const arg = args[i];
|
|
26
|
-
if (!arg) continue; // Skip if undefined
|
|
27
|
-
|
|
28
|
-
// Separator for program arguments
|
|
29
|
-
if (arg === "--") {
|
|
30
|
-
captureProgramArgs = true;
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (captureProgramArgs) {
|
|
35
|
-
programArgs.push(arg);
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Commands
|
|
40
|
-
if (!command && !arg.startsWith("-")) {
|
|
41
|
-
command = arg;
|
|
42
|
-
// Handle "project init" as two-word command
|
|
43
|
-
const nextArg = args[i + 1];
|
|
44
|
-
if (command === "project" && nextArg === "init") {
|
|
45
|
-
command = "project:init";
|
|
46
|
-
i++;
|
|
47
|
-
}
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Entry file (first non-option after command)
|
|
52
|
-
if (command && !entryFile && !arg.startsWith("-")) {
|
|
53
|
-
entryFile = arg;
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Options
|
|
58
|
-
switch (arg) {
|
|
59
|
-
case "-h":
|
|
60
|
-
case "--help":
|
|
61
|
-
options.verbose = true; // reuse for help flag
|
|
62
|
-
return { command: "help", options: {} };
|
|
63
|
-
case "-v":
|
|
64
|
-
case "--version":
|
|
65
|
-
return { command: "version", options: {} };
|
|
66
|
-
case "-V":
|
|
67
|
-
case "--verbose":
|
|
68
|
-
options.verbose = true;
|
|
69
|
-
break;
|
|
70
|
-
case "-q":
|
|
71
|
-
case "--quiet":
|
|
72
|
-
options.quiet = true;
|
|
73
|
-
break;
|
|
74
|
-
case "-c":
|
|
75
|
-
case "--config":
|
|
76
|
-
options.config = args[++i] ?? "";
|
|
77
|
-
break;
|
|
78
|
-
case "-s":
|
|
79
|
-
case "--src":
|
|
80
|
-
options.src = args[++i] ?? "";
|
|
81
|
-
break;
|
|
82
|
-
case "-o":
|
|
83
|
-
case "--out":
|
|
84
|
-
options.out = args[++i] ?? "";
|
|
85
|
-
break;
|
|
86
|
-
case "-n":
|
|
87
|
-
case "--namespace":
|
|
88
|
-
options.namespace = args[++i] ?? "";
|
|
89
|
-
break;
|
|
90
|
-
case "-r":
|
|
91
|
-
case "--rid":
|
|
92
|
-
options.rid = args[++i] ?? "";
|
|
93
|
-
break;
|
|
94
|
-
case "--runtime":
|
|
95
|
-
options.runtime = (args[++i] ?? "js") as "js" | "dotnet";
|
|
96
|
-
break;
|
|
97
|
-
case "--skip-types":
|
|
98
|
-
options.skipTypes = true;
|
|
99
|
-
break;
|
|
100
|
-
case "--types-version":
|
|
101
|
-
options.typesVersion = args[++i] ?? "";
|
|
102
|
-
break;
|
|
103
|
-
case "-O":
|
|
104
|
-
case "--optimize":
|
|
105
|
-
options.optimize = (args[++i] ?? "speed") as "size" | "speed";
|
|
106
|
-
break;
|
|
107
|
-
case "-k":
|
|
108
|
-
case "--keep-temp":
|
|
109
|
-
options.keepTemp = true;
|
|
110
|
-
break;
|
|
111
|
-
case "--no-strip":
|
|
112
|
-
options.noStrip = true;
|
|
113
|
-
break;
|
|
114
|
-
case "-L":
|
|
115
|
-
case "--lib":
|
|
116
|
-
{
|
|
117
|
-
const libPath = args[++i] ?? "";
|
|
118
|
-
if (libPath) {
|
|
119
|
-
options.lib = options.lib || [];
|
|
120
|
-
options.lib.push(libPath);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
break;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return { command, entryFile, options, programArgs };
|
|
128
|
-
};
|
package/src/cli.ts
DELETED
package/src/commands/build.ts
DELETED
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* tsonic build command - Build executable or library
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { spawnSync } from "node:child_process";
|
|
6
|
-
import { join, relative } from "node:path";
|
|
7
|
-
import { copyFileSync, chmodSync, existsSync, mkdirSync } from "node:fs";
|
|
8
|
-
import type { ResolvedConfig, Result } from "../types.js";
|
|
9
|
-
import { emitCommand } from "./emit.js";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Build native executable
|
|
13
|
-
*/
|
|
14
|
-
const buildExecutable = (
|
|
15
|
-
config: ResolvedConfig,
|
|
16
|
-
generatedDir: string
|
|
17
|
-
): Result<{ outputPath: string }, string> => {
|
|
18
|
-
const { outputName, rid, quiet, verbose } = config;
|
|
19
|
-
|
|
20
|
-
// Step 2: Run dotnet publish
|
|
21
|
-
if (!quiet) {
|
|
22
|
-
console.log("Step 2/3: Compiling with dotnet publish...");
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const publishArgs = [
|
|
26
|
-
"publish",
|
|
27
|
-
"tsonic.csproj",
|
|
28
|
-
"-c",
|
|
29
|
-
"Release",
|
|
30
|
-
"-r",
|
|
31
|
-
rid,
|
|
32
|
-
"--nologo",
|
|
33
|
-
];
|
|
34
|
-
|
|
35
|
-
if (quiet) {
|
|
36
|
-
publishArgs.push("--verbosity", "quiet");
|
|
37
|
-
} else if (verbose) {
|
|
38
|
-
publishArgs.push("--verbosity", "detailed");
|
|
39
|
-
} else {
|
|
40
|
-
publishArgs.push("--verbosity", "minimal");
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const publishResult = spawnSync("dotnet", publishArgs, {
|
|
44
|
-
cwd: generatedDir,
|
|
45
|
-
stdio: verbose ? "inherit" : "pipe",
|
|
46
|
-
encoding: "utf-8",
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
if (publishResult.status !== 0) {
|
|
50
|
-
const errorMsg =
|
|
51
|
-
publishResult.stderr || publishResult.stdout || "Unknown error";
|
|
52
|
-
return {
|
|
53
|
-
ok: false,
|
|
54
|
-
error: `dotnet publish failed:\n${errorMsg}`,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Step 3: Copy output binary
|
|
59
|
-
if (!quiet) {
|
|
60
|
-
console.log("Step 3/3: Copying output binary...");
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const binaryName =
|
|
64
|
-
process.platform === "win32" ? `${outputName}.exe` : outputName;
|
|
65
|
-
const publishDir = join(
|
|
66
|
-
generatedDir,
|
|
67
|
-
"bin",
|
|
68
|
-
"Release",
|
|
69
|
-
config.dotnetVersion,
|
|
70
|
-
rid,
|
|
71
|
-
"publish"
|
|
72
|
-
);
|
|
73
|
-
const sourceBinary = join(publishDir, binaryName);
|
|
74
|
-
const targetBinary = join(process.cwd(), binaryName);
|
|
75
|
-
|
|
76
|
-
if (!existsSync(sourceBinary)) {
|
|
77
|
-
return {
|
|
78
|
-
ok: false,
|
|
79
|
-
error: `Built binary not found at ${sourceBinary}`,
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
try {
|
|
84
|
-
copyFileSync(sourceBinary, targetBinary);
|
|
85
|
-
|
|
86
|
-
// Make executable on Unix
|
|
87
|
-
if (process.platform !== "win32") {
|
|
88
|
-
chmodSync(targetBinary, 0o755);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (!quiet) {
|
|
92
|
-
const relativePath = relative(process.cwd(), targetBinary);
|
|
93
|
-
console.log(`\n✓ Build complete: ${relativePath}`);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return {
|
|
97
|
-
ok: true,
|
|
98
|
-
value: { outputPath: targetBinary },
|
|
99
|
-
};
|
|
100
|
-
} catch (error) {
|
|
101
|
-
return {
|
|
102
|
-
ok: false,
|
|
103
|
-
error: `Failed to copy binary: ${error instanceof Error ? error.message : String(error)}`,
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Build library
|
|
110
|
-
*/
|
|
111
|
-
const buildLibrary = (
|
|
112
|
-
config: ResolvedConfig,
|
|
113
|
-
generatedDir: string
|
|
114
|
-
): Result<{ outputPath: string }, string> => {
|
|
115
|
-
const { outputName, quiet, verbose } = config;
|
|
116
|
-
const targetFrameworks = config.outputConfig.targetFrameworks ?? [
|
|
117
|
-
config.dotnetVersion,
|
|
118
|
-
];
|
|
119
|
-
|
|
120
|
-
// Step 2: Run dotnet build
|
|
121
|
-
if (!quiet) {
|
|
122
|
-
console.log("Step 2/3: Compiling library with dotnet build...");
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const buildArgs = ["build", "tsonic.csproj", "-c", "Release", "--nologo"];
|
|
126
|
-
|
|
127
|
-
if (quiet) {
|
|
128
|
-
buildArgs.push("--verbosity", "quiet");
|
|
129
|
-
} else if (verbose) {
|
|
130
|
-
buildArgs.push("--verbosity", "detailed");
|
|
131
|
-
} else {
|
|
132
|
-
buildArgs.push("--verbosity", "minimal");
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const buildResult = spawnSync("dotnet", buildArgs, {
|
|
136
|
-
cwd: generatedDir,
|
|
137
|
-
stdio: verbose ? "inherit" : "pipe",
|
|
138
|
-
encoding: "utf-8",
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
if (buildResult.status !== 0) {
|
|
142
|
-
const errorMsg =
|
|
143
|
-
buildResult.stderr || buildResult.stdout || "Unknown error";
|
|
144
|
-
return {
|
|
145
|
-
ok: false,
|
|
146
|
-
error: `dotnet build failed:\n${errorMsg}`,
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Step 3: Copy output library artifacts
|
|
151
|
-
if (!quiet) {
|
|
152
|
-
console.log("Step 3/3: Copying library artifacts...");
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const outputDir = join(process.cwd(), "dist");
|
|
156
|
-
|
|
157
|
-
try {
|
|
158
|
-
// Create output directory
|
|
159
|
-
if (!existsSync(outputDir)) {
|
|
160
|
-
mkdirSync(outputDir, { recursive: true });
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Copy artifacts for each target framework
|
|
164
|
-
const copiedFiles: string[] = [];
|
|
165
|
-
|
|
166
|
-
for (const framework of targetFrameworks) {
|
|
167
|
-
const buildDir = join(generatedDir, "bin", "Release", framework);
|
|
168
|
-
|
|
169
|
-
if (!existsSync(buildDir)) {
|
|
170
|
-
continue;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const frameworkOutputDir = join(outputDir, framework);
|
|
174
|
-
if (!existsSync(frameworkOutputDir)) {
|
|
175
|
-
mkdirSync(frameworkOutputDir, { recursive: true });
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Copy .dll
|
|
179
|
-
const dllSource = join(buildDir, `${outputName}.dll`);
|
|
180
|
-
if (existsSync(dllSource)) {
|
|
181
|
-
const dllTarget = join(frameworkOutputDir, `${outputName}.dll`);
|
|
182
|
-
copyFileSync(dllSource, dllTarget);
|
|
183
|
-
copiedFiles.push(relative(process.cwd(), dllTarget));
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Copy .xml (documentation)
|
|
187
|
-
const xmlSource = join(buildDir, `${outputName}.xml`);
|
|
188
|
-
if (existsSync(xmlSource)) {
|
|
189
|
-
const xmlTarget = join(frameworkOutputDir, `${outputName}.xml`);
|
|
190
|
-
copyFileSync(xmlSource, xmlTarget);
|
|
191
|
-
copiedFiles.push(relative(process.cwd(), xmlTarget));
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Copy .pdb (symbols)
|
|
195
|
-
const pdbSource = join(buildDir, `${outputName}.pdb`);
|
|
196
|
-
if (existsSync(pdbSource)) {
|
|
197
|
-
const pdbTarget = join(frameworkOutputDir, `${outputName}.pdb`);
|
|
198
|
-
copyFileSync(pdbSource, pdbTarget);
|
|
199
|
-
copiedFiles.push(relative(process.cwd(), pdbTarget));
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
if (copiedFiles.length === 0) {
|
|
204
|
-
return {
|
|
205
|
-
ok: false,
|
|
206
|
-
error: "No library artifacts found to copy",
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
if (!quiet) {
|
|
211
|
-
console.log(`\n✓ Build complete. Artifacts copied to dist/:`);
|
|
212
|
-
for (const file of copiedFiles) {
|
|
213
|
-
console.log(` - ${file}`);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
return {
|
|
218
|
-
ok: true,
|
|
219
|
-
value: { outputPath: outputDir },
|
|
220
|
-
};
|
|
221
|
-
} catch (error) {
|
|
222
|
-
return {
|
|
223
|
-
ok: false,
|
|
224
|
-
error: `Failed to copy library artifacts: ${error instanceof Error ? error.message : String(error)}`,
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Main build command - dispatches to executable or library build
|
|
231
|
-
*/
|
|
232
|
-
export const buildCommand = (
|
|
233
|
-
config: ResolvedConfig
|
|
234
|
-
): Result<{ outputPath: string }, string> => {
|
|
235
|
-
const { outputDirectory, quiet } = config;
|
|
236
|
-
const outputType = config.outputConfig.type ?? "executable";
|
|
237
|
-
|
|
238
|
-
// Step 1: Emit C# code
|
|
239
|
-
if (!quiet) {
|
|
240
|
-
console.log("Step 1/3: Generating C# code...");
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
const emitResult = emitCommand(config);
|
|
244
|
-
if (!emitResult.ok) {
|
|
245
|
-
return emitResult;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
const generatedDir = emitResult.value.outputDir;
|
|
249
|
-
const csprojPath = join(generatedDir, "tsonic.csproj");
|
|
250
|
-
|
|
251
|
-
if (!existsSync(csprojPath)) {
|
|
252
|
-
return {
|
|
253
|
-
ok: false,
|
|
254
|
-
error: `No tsonic.csproj found in ${outputDirectory}/. This should have been created by emit.`,
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// Dispatch to appropriate build function
|
|
259
|
-
if (outputType === "library") {
|
|
260
|
-
return buildLibrary(config, generatedDir);
|
|
261
|
-
} else {
|
|
262
|
-
return buildExecutable(config, generatedDir);
|
|
263
|
-
}
|
|
264
|
-
};
|