@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.
- package/dist/.tsbuildinfo +1 -0
- package/dist/commands/init.js +1 -1
- package/package.json +7 -4
- 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/commands/emit.ts
DELETED
|
@@ -1,406 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* tsonic emit command - Generate C# code only
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
mkdirSync,
|
|
7
|
-
writeFileSync,
|
|
8
|
-
existsSync,
|
|
9
|
-
readdirSync,
|
|
10
|
-
copyFileSync,
|
|
11
|
-
} from "node:fs";
|
|
12
|
-
import { join, dirname, relative, resolve } from "node:path";
|
|
13
|
-
import {
|
|
14
|
-
buildModuleDependencyGraph,
|
|
15
|
-
type Diagnostic,
|
|
16
|
-
type IrModule,
|
|
17
|
-
type CompilerOptions,
|
|
18
|
-
} from "@tsonic/frontend";
|
|
19
|
-
import { emitCSharpFiles } from "@tsonic/emitter";
|
|
20
|
-
import {
|
|
21
|
-
generateCsproj,
|
|
22
|
-
generateProgramCs,
|
|
23
|
-
type EntryInfo,
|
|
24
|
-
type BuildConfig,
|
|
25
|
-
type ExecutableConfig,
|
|
26
|
-
type LibraryConfig,
|
|
27
|
-
type AssemblyReference,
|
|
28
|
-
} from "@tsonic/backend";
|
|
29
|
-
import type { ResolvedConfig, Result } from "../types.js";
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Find project .csproj file in current directory
|
|
33
|
-
*/
|
|
34
|
-
const findProjectCsproj = (): string | null => {
|
|
35
|
-
const cwd = process.cwd();
|
|
36
|
-
const files = readdirSync(cwd);
|
|
37
|
-
const csprojFile = files.find((f) => f.endsWith(".csproj"));
|
|
38
|
-
return csprojFile ? join(cwd, csprojFile) : null;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Find runtime DLLs from @tsonic/tsonic npm package
|
|
43
|
-
* Returns assembly references for the csproj file
|
|
44
|
-
*/
|
|
45
|
-
const findRuntimeDlls = (
|
|
46
|
-
runtime: "js" | "dotnet",
|
|
47
|
-
outputDir: string
|
|
48
|
-
): readonly AssemblyReference[] => {
|
|
49
|
-
// Try to find @tsonic/tsonic package runtime directory
|
|
50
|
-
const possiblePaths = [
|
|
51
|
-
// From project's node_modules
|
|
52
|
-
join(process.cwd(), "node_modules/@tsonic/tsonic/runtime"),
|
|
53
|
-
// From CLI's node_modules (when installed globally or via npx)
|
|
54
|
-
join(import.meta.dirname, "../../runtime"),
|
|
55
|
-
join(import.meta.dirname, "../../../runtime"),
|
|
56
|
-
];
|
|
57
|
-
|
|
58
|
-
let runtimeDir: string | null = null;
|
|
59
|
-
for (const p of possiblePaths) {
|
|
60
|
-
if (existsSync(p)) {
|
|
61
|
-
runtimeDir = p;
|
|
62
|
-
break;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (!runtimeDir) {
|
|
67
|
-
return [];
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const refs: AssemblyReference[] = [];
|
|
71
|
-
|
|
72
|
-
// Calculate relative path from output directory to runtime directory
|
|
73
|
-
const relativeRuntimeDir = relative(outputDir, runtimeDir);
|
|
74
|
-
|
|
75
|
-
// Always include Tsonic.Runtime
|
|
76
|
-
const runtimeDll = join(runtimeDir, "Tsonic.Runtime.dll");
|
|
77
|
-
if (existsSync(runtimeDll)) {
|
|
78
|
-
refs.push({
|
|
79
|
-
name: "Tsonic.Runtime",
|
|
80
|
-
hintPath: join(relativeRuntimeDir, "Tsonic.Runtime.dll"),
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Include Tsonic.JSRuntime for js mode
|
|
85
|
-
if (runtime === "js") {
|
|
86
|
-
const jsRuntimeDll = join(runtimeDir, "Tsonic.JSRuntime.dll");
|
|
87
|
-
if (existsSync(jsRuntimeDll)) {
|
|
88
|
-
refs.push({
|
|
89
|
-
name: "Tsonic.JSRuntime",
|
|
90
|
-
hintPath: join(relativeRuntimeDir, "Tsonic.JSRuntime.dll"),
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return refs;
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Extract entry point information from IR module
|
|
100
|
-
*/
|
|
101
|
-
const extractEntryInfo = (
|
|
102
|
-
entryModule: IrModule,
|
|
103
|
-
runtime?: "js" | "dotnet"
|
|
104
|
-
): EntryInfo | null => {
|
|
105
|
-
// Look for exported 'main' function
|
|
106
|
-
for (const exp of entryModule.exports) {
|
|
107
|
-
if (exp.kind === "declaration") {
|
|
108
|
-
const decl = exp.declaration;
|
|
109
|
-
if (decl.kind === "functionDeclaration" && decl.name === "main") {
|
|
110
|
-
return {
|
|
111
|
-
namespace: entryModule.namespace,
|
|
112
|
-
className: entryModule.className,
|
|
113
|
-
methodName: "main",
|
|
114
|
-
isAsync: decl.isAsync,
|
|
115
|
-
needsProgram: true,
|
|
116
|
-
runtime,
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
} else if (exp.kind === "named" && exp.name === "main") {
|
|
120
|
-
// Named export of 'main'
|
|
121
|
-
// Look in body for the function declaration
|
|
122
|
-
for (const stmt of entryModule.body) {
|
|
123
|
-
if (stmt.kind === "functionDeclaration" && stmt.name === "main") {
|
|
124
|
-
return {
|
|
125
|
-
namespace: entryModule.namespace,
|
|
126
|
-
className: entryModule.className,
|
|
127
|
-
methodName: "main",
|
|
128
|
-
isAsync: stmt.isAsync,
|
|
129
|
-
needsProgram: true,
|
|
130
|
-
runtime,
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// No main function found
|
|
138
|
-
return null;
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Emit C# code from TypeScript
|
|
143
|
-
*/
|
|
144
|
-
export const emitCommand = (
|
|
145
|
-
config: ResolvedConfig
|
|
146
|
-
): Result<{ filesGenerated: number; outputDir: string }, string> => {
|
|
147
|
-
const {
|
|
148
|
-
entryPoint,
|
|
149
|
-
outputDirectory,
|
|
150
|
-
rootNamespace,
|
|
151
|
-
sourceRoot,
|
|
152
|
-
packages,
|
|
153
|
-
typeRoots,
|
|
154
|
-
} = config;
|
|
155
|
-
|
|
156
|
-
// For libraries, entry point is optional
|
|
157
|
-
if (!entryPoint && config.outputConfig.type !== "library") {
|
|
158
|
-
return {
|
|
159
|
-
ok: false,
|
|
160
|
-
error: "Entry point is required for executable builds",
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if (!config.quiet) {
|
|
165
|
-
const target = entryPoint ?? sourceRoot;
|
|
166
|
-
console.log(`Emitting C# code for ${target}...`);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
try {
|
|
170
|
-
// For libraries without entry point, we need a different approach
|
|
171
|
-
// For now, require entry point (library multi-file support can be added later)
|
|
172
|
-
if (!entryPoint) {
|
|
173
|
-
return {
|
|
174
|
-
ok: false,
|
|
175
|
-
error:
|
|
176
|
-
"Entry point is required (library multi-file support coming soon)",
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// Combine typeRoots and libraries for TypeScript compilation
|
|
181
|
-
const allTypeRoots = [...typeRoots, ...config.libraries];
|
|
182
|
-
|
|
183
|
-
// Build dependency graph - this traverses all imports and builds IR for all modules
|
|
184
|
-
const compilerOptions: CompilerOptions = {
|
|
185
|
-
sourceRoot,
|
|
186
|
-
rootNamespace,
|
|
187
|
-
typeRoots: allTypeRoots,
|
|
188
|
-
verbose: config.verbose,
|
|
189
|
-
};
|
|
190
|
-
const graphResult = buildModuleDependencyGraph(entryPoint, compilerOptions);
|
|
191
|
-
|
|
192
|
-
if (!graphResult.ok) {
|
|
193
|
-
const errorMessages = graphResult.error
|
|
194
|
-
.map((d: Diagnostic) => {
|
|
195
|
-
if (d.location) {
|
|
196
|
-
return `${d.location.file}:${d.location.line} ${d.message}`;
|
|
197
|
-
}
|
|
198
|
-
return d.message;
|
|
199
|
-
})
|
|
200
|
-
.join("\n");
|
|
201
|
-
return {
|
|
202
|
-
ok: false,
|
|
203
|
-
error: `TypeScript compilation failed:\n${errorMessages}`,
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const { modules, entryModule } = graphResult.value;
|
|
208
|
-
|
|
209
|
-
if (config.verbose) {
|
|
210
|
-
console.log(` Discovered ${modules.length} TypeScript modules`);
|
|
211
|
-
for (const module of modules) {
|
|
212
|
-
console.log(` - ${module.filePath}`);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// irResult.value was an array of modules, now it's graphResult.value.modules
|
|
217
|
-
const irResult = { ok: true as const, value: modules };
|
|
218
|
-
|
|
219
|
-
// Emit C# code
|
|
220
|
-
const absoluteEntryPoint = entryPoint ? resolve(entryPoint) : undefined;
|
|
221
|
-
const emitResult = emitCSharpFiles(irResult.value, {
|
|
222
|
-
rootNamespace,
|
|
223
|
-
entryPointPath: absoluteEntryPoint,
|
|
224
|
-
libraries: config.libraries,
|
|
225
|
-
runtime: config.runtime,
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
if (!emitResult.ok) {
|
|
229
|
-
// Handle file name collision errors
|
|
230
|
-
for (const error of emitResult.errors) {
|
|
231
|
-
console.error(`error ${error.code}: ${error.message}`);
|
|
232
|
-
}
|
|
233
|
-
process.exit(1);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
const csFiles = emitResult.files;
|
|
237
|
-
|
|
238
|
-
// Create output directory
|
|
239
|
-
const outputDir = join(process.cwd(), outputDirectory);
|
|
240
|
-
mkdirSync(outputDir, { recursive: true });
|
|
241
|
-
|
|
242
|
-
// Write C# files preserving directory structure
|
|
243
|
-
for (const [modulePath, csCode] of csFiles) {
|
|
244
|
-
// Convert module path to C# file path
|
|
245
|
-
// src/models/User.ts → generated/src/models/User.cs
|
|
246
|
-
const csPath = modulePath.replace(/\.ts$/, ".cs");
|
|
247
|
-
const fullPath = join(outputDir, csPath);
|
|
248
|
-
|
|
249
|
-
mkdirSync(dirname(fullPath), { recursive: true });
|
|
250
|
-
writeFileSync(fullPath, csCode, "utf-8");
|
|
251
|
-
|
|
252
|
-
if (config.verbose) {
|
|
253
|
-
const relPath = relative(process.cwd(), fullPath);
|
|
254
|
-
console.log(` Generated: ${relPath}`);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// Generate Program.cs entry point wrapper (only for executables)
|
|
259
|
-
if (absoluteEntryPoint) {
|
|
260
|
-
// entryModule is already provided by buildDependencyGraph
|
|
261
|
-
// But double-check by comparing relative paths
|
|
262
|
-
const entryRelative = relative(sourceRoot, absoluteEntryPoint).replace(
|
|
263
|
-
/\\/g,
|
|
264
|
-
"/"
|
|
265
|
-
);
|
|
266
|
-
const foundEntryModule =
|
|
267
|
-
irResult.value.find((m: IrModule) => m.filePath === entryRelative) ??
|
|
268
|
-
entryModule;
|
|
269
|
-
|
|
270
|
-
if (foundEntryModule) {
|
|
271
|
-
const entryInfo = extractEntryInfo(foundEntryModule, config.runtime);
|
|
272
|
-
if (entryInfo) {
|
|
273
|
-
const programCs = generateProgramCs(entryInfo);
|
|
274
|
-
const programPath = join(outputDir, "Program.cs");
|
|
275
|
-
writeFileSync(programPath, programCs, "utf-8");
|
|
276
|
-
|
|
277
|
-
if (config.verbose) {
|
|
278
|
-
console.log(` Generated: ${relative(process.cwd(), programPath)}`);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// Generate or copy existing .csproj
|
|
285
|
-
const csprojPath = join(outputDir, "tsonic.csproj");
|
|
286
|
-
const projectCsproj = findProjectCsproj();
|
|
287
|
-
|
|
288
|
-
if (projectCsproj) {
|
|
289
|
-
// Copy existing .csproj from project root (preserves user edits)
|
|
290
|
-
copyFileSync(projectCsproj, csprojPath);
|
|
291
|
-
|
|
292
|
-
if (config.verbose) {
|
|
293
|
-
console.log(
|
|
294
|
-
` Copied: ${relative(process.cwd(), projectCsproj)} → ${relative(process.cwd(), csprojPath)} (user edits preserved)`
|
|
295
|
-
);
|
|
296
|
-
}
|
|
297
|
-
} else if (!existsSync(csprojPath)) {
|
|
298
|
-
// Find Tsonic runtime - try multiple approaches:
|
|
299
|
-
// 1. ProjectReference to .csproj (development/monorepo)
|
|
300
|
-
// 2. Assembly references to DLLs (npm installed package)
|
|
301
|
-
let runtimePath: string | undefined;
|
|
302
|
-
let assemblyReferences: readonly AssemblyReference[] = [];
|
|
303
|
-
|
|
304
|
-
// 1. Try monorepo structure (development) - ProjectReference
|
|
305
|
-
const monorepoPath = resolve(
|
|
306
|
-
join(import.meta.dirname, "../../../runtime/src/Tsonic.Runtime.csproj")
|
|
307
|
-
);
|
|
308
|
-
if (existsSync(monorepoPath)) {
|
|
309
|
-
runtimePath = monorepoPath;
|
|
310
|
-
} else {
|
|
311
|
-
// 2. Try installed package structure - ProjectReference
|
|
312
|
-
const installedPath = resolve(
|
|
313
|
-
join(
|
|
314
|
-
import.meta.dirname,
|
|
315
|
-
"../../../../@tsonic/runtime/src/Tsonic.Runtime.csproj"
|
|
316
|
-
)
|
|
317
|
-
);
|
|
318
|
-
if (existsSync(installedPath)) {
|
|
319
|
-
runtimePath = installedPath;
|
|
320
|
-
} else {
|
|
321
|
-
// 3. Try to find runtime DLLs from npm package
|
|
322
|
-
assemblyReferences = findRuntimeDlls(
|
|
323
|
-
config.runtime ?? "js",
|
|
324
|
-
outputDir
|
|
325
|
-
);
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// Warn if no runtime found
|
|
330
|
-
if (!runtimePath && assemblyReferences.length === 0 && !config.quiet) {
|
|
331
|
-
console.warn(
|
|
332
|
-
"Warning: Tsonic runtime not found. You may need to add references manually."
|
|
333
|
-
);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// Build output configuration
|
|
337
|
-
const outputType = config.outputConfig.type ?? "executable";
|
|
338
|
-
let outputConfig: ExecutableConfig | LibraryConfig;
|
|
339
|
-
|
|
340
|
-
if (outputType === "library") {
|
|
341
|
-
outputConfig = {
|
|
342
|
-
type: "library",
|
|
343
|
-
targetFrameworks: config.outputConfig.targetFrameworks ?? [
|
|
344
|
-
config.dotnetVersion,
|
|
345
|
-
],
|
|
346
|
-
generateDocumentation:
|
|
347
|
-
config.outputConfig.generateDocumentation ?? true,
|
|
348
|
-
includeSymbols: config.outputConfig.includeSymbols ?? true,
|
|
349
|
-
packable: config.outputConfig.packable ?? false,
|
|
350
|
-
packageMetadata: config.outputConfig.package,
|
|
351
|
-
};
|
|
352
|
-
} else {
|
|
353
|
-
outputConfig = {
|
|
354
|
-
type: "executable",
|
|
355
|
-
nativeAot: config.outputConfig.nativeAot ?? true,
|
|
356
|
-
singleFile: config.outputConfig.singleFile ?? true,
|
|
357
|
-
trimmed: config.outputConfig.trimmed ?? true,
|
|
358
|
-
stripSymbols: config.stripSymbols,
|
|
359
|
-
optimization: config.optimize === "size" ? "Size" : "Speed",
|
|
360
|
-
invariantGlobalization: config.invariantGlobalization,
|
|
361
|
-
selfContained: config.outputConfig.selfContained ?? true,
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
const buildConfig: BuildConfig = {
|
|
366
|
-
rootNamespace,
|
|
367
|
-
outputName: config.outputName,
|
|
368
|
-
dotnetVersion: config.dotnetVersion,
|
|
369
|
-
runtimePath,
|
|
370
|
-
assemblyReferences,
|
|
371
|
-
packages,
|
|
372
|
-
outputConfig,
|
|
373
|
-
};
|
|
374
|
-
|
|
375
|
-
const csproj = generateCsproj(buildConfig);
|
|
376
|
-
writeFileSync(csprojPath, csproj, "utf-8");
|
|
377
|
-
|
|
378
|
-
if (config.verbose) {
|
|
379
|
-
console.log(` Generated: ${relative(process.cwd(), csprojPath)}`);
|
|
380
|
-
}
|
|
381
|
-
} else if (config.verbose) {
|
|
382
|
-
console.log(
|
|
383
|
-
` Preserved: ${relative(process.cwd(), csprojPath)} (user edits kept)`
|
|
384
|
-
);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
if (!config.quiet) {
|
|
388
|
-
console.log(
|
|
389
|
-
`\n✓ Generated ${csFiles.size} C# files in ${outputDirectory}/`
|
|
390
|
-
);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
return {
|
|
394
|
-
ok: true,
|
|
395
|
-
value: {
|
|
396
|
-
filesGenerated: csFiles.size,
|
|
397
|
-
outputDir,
|
|
398
|
-
},
|
|
399
|
-
};
|
|
400
|
-
} catch (error) {
|
|
401
|
-
return {
|
|
402
|
-
ok: false,
|
|
403
|
-
error: `Emit failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
404
|
-
};
|
|
405
|
-
}
|
|
406
|
-
};
|