@tsonic/cli 0.0.19 → 0.0.21
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 -1
- package/dist/cli/dispatcher.d.ts.map +1 -1
- package/dist/cli/dispatcher.js +96 -4
- package/dist/cli/dispatcher.js.map +1 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +8 -0
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/parser.d.ts.map +1 -1
- package/dist/cli/parser.js +17 -0
- package/dist/cli/parser.js.map +1 -1
- package/dist/cli/parser.test.js +25 -0
- package/dist/cli/parser.test.js.map +1 -1
- package/dist/commands/add-common.d.ts +19 -4
- package/dist/commands/add-common.d.ts.map +1 -1
- package/dist/commands/add-common.js +115 -20
- package/dist/commands/add-common.js.map +1 -1
- package/dist/commands/add-deps.test.d.ts +10 -0
- package/dist/commands/add-deps.test.d.ts.map +1 -0
- package/dist/commands/add-deps.test.js +277 -0
- package/dist/commands/add-deps.test.js.map +1 -0
- package/dist/commands/add-framework.d.ts +1 -1
- package/dist/commands/add-framework.d.ts.map +1 -1
- package/dist/commands/add-framework.js +53 -35
- package/dist/commands/add-framework.js.map +1 -1
- package/dist/commands/add-nuget.d.ts +6 -3
- package/dist/commands/add-nuget.d.ts.map +1 -1
- package/dist/commands/add-nuget.js +49 -211
- package/dist/commands/add-nuget.js.map +1 -1
- package/dist/commands/add-package.d.ts +1 -1
- package/dist/commands/add-package.d.ts.map +1 -1
- package/dist/commands/add-package.js +162 -58
- package/dist/commands/add-package.js.map +1 -1
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +5 -1
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/remove-nuget.d.ts +16 -0
- package/dist/commands/remove-nuget.d.ts.map +1 -0
- package/dist/commands/remove-nuget.js +47 -0
- package/dist/commands/remove-nuget.js.map +1 -0
- package/dist/commands/restore.d.ts +19 -0
- package/dist/commands/restore.d.ts.map +1 -0
- package/dist/commands/restore.js +619 -0
- package/dist/commands/restore.js.map +1 -0
- package/dist/commands/restore.test.d.ts +5 -0
- package/dist/commands/restore.test.d.ts.map +1 -0
- package/dist/commands/restore.test.js +59 -0
- package/dist/commands/restore.test.js.map +1 -0
- package/dist/commands/update-nuget.d.ts +18 -0
- package/dist/commands/update-nuget.d.ts.map +1 -0
- package/dist/commands/update-nuget.js +76 -0
- package/dist/commands/update-nuget.js.map +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +24 -6
- package/dist/config.js.map +1 -1
- package/dist/dotnet/runtime-dlls.d.ts +3 -0
- package/dist/dotnet/runtime-dlls.d.ts.map +1 -0
- package/dist/dotnet/runtime-dlls.js +5 -0
- package/dist/dotnet/runtime-dlls.js.map +1 -0
- package/dist/types.d.ts +13 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -4
|
@@ -4,123 +4,67 @@
|
|
|
4
4
|
* Usage:
|
|
5
5
|
* tsonic add nuget <PackageId> <Version> [typesPackage]
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Airplane-grade rules:
|
|
8
|
+
* - Versions are pinned (no automatic upgrades/downgrades)
|
|
9
|
+
* - If a types package is provided, we do NOT auto-generate bindings
|
|
10
|
+
* - If types are omitted, we auto-generate bindings for the full transitive closure
|
|
11
|
+
* via `tsonic restore` (single source of truth)
|
|
9
12
|
*/
|
|
10
|
-
import {
|
|
11
|
-
import { dirname
|
|
12
|
-
import { defaultBindingsPackageNameForNuget,
|
|
13
|
+
import { loadConfig } from "../config.js";
|
|
14
|
+
import { dirname } from "node:path";
|
|
15
|
+
import { defaultBindingsPackageNameForNuget, npmInstallDevDependency, writeTsonicJson, } from "./add-common.js";
|
|
16
|
+
import { restoreCommand } from "./restore.js";
|
|
17
|
+
const normalizePkgId = (id) => id.trim().toLowerCase();
|
|
13
18
|
const isValidTypesPackageName = (name) => {
|
|
14
19
|
if (!name.startsWith("@") && !name.includes("/"))
|
|
15
20
|
return true;
|
|
16
21
|
return /^@[a-z0-9-]+\/[a-z0-9-]+$/i.test(name);
|
|
17
22
|
};
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const csprojPath = join(restoreDir, "tsonic.nuget.restore.csproj");
|
|
23
|
-
const itemGroup = packageReferences
|
|
24
|
-
.map((p) => ` <PackageReference Include="${p.id}" Version="${p.version}" />`)
|
|
25
|
-
.join("\n");
|
|
26
|
-
const csproj = `<Project Sdk="Microsoft.NET.Sdk">
|
|
27
|
-
<PropertyGroup>
|
|
28
|
-
<TargetFramework>${targetFramework}</TargetFramework>
|
|
29
|
-
<ImplicitUsings>false</ImplicitUsings>
|
|
30
|
-
<Nullable>enable</Nullable>
|
|
31
|
-
</PropertyGroup>
|
|
32
|
-
<ItemGroup>
|
|
33
|
-
${itemGroup}
|
|
34
|
-
</ItemGroup>
|
|
35
|
-
</Project>
|
|
36
|
-
`;
|
|
37
|
-
writeFileSync(csprojPath, csproj, "utf-8");
|
|
38
|
-
return { ok: true, value: csprojPath };
|
|
39
|
-
}
|
|
40
|
-
catch (error) {
|
|
41
|
-
return {
|
|
42
|
-
ok: false,
|
|
43
|
-
error: `Failed to write restore project: ${error instanceof Error ? error.message : String(error)}`,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
const dotnetRestore = (restoreProjectPath, options, exec = defaultExec) => {
|
|
48
|
-
const restoreDir = dirname(restoreProjectPath);
|
|
49
|
-
const result = exec("dotnet", ["restore", restoreProjectPath], restoreDir, options.verbose ? "inherit" : "pipe");
|
|
50
|
-
if (result.status !== 0) {
|
|
51
|
-
const msg = result.stderr || result.stdout || "Unknown error";
|
|
52
|
-
return { ok: false, error: `dotnet restore failed:\n${msg}` };
|
|
53
|
-
}
|
|
54
|
-
const assetsPath = join(restoreDir, "obj", "project.assets.json");
|
|
55
|
-
if (!existsSync(assetsPath)) {
|
|
56
|
-
return { ok: false, error: `Restore succeeded but assets file missing: ${assetsPath}` };
|
|
23
|
+
export const addNugetCommand = (id, ver, typesPackage, configPath, options = {}) => {
|
|
24
|
+
const projectRoot = dirname(configPath);
|
|
25
|
+
if (!id.trim()) {
|
|
26
|
+
return { ok: false, error: "NuGet package id must be non-empty" };
|
|
57
27
|
}
|
|
58
|
-
|
|
59
|
-
};
|
|
60
|
-
const pickPackageFolder = (assets) => {
|
|
61
|
-
const folders = assets.packageFolders ? Object.keys(assets.packageFolders) : [];
|
|
62
|
-
if (folders.length === 0)
|
|
63
|
-
return undefined;
|
|
64
|
-
// Use the first package folder entry (dotnet restore writes an ordered map).
|
|
65
|
-
return folders[0];
|
|
66
|
-
};
|
|
67
|
-
const findTargetKey = (assets, tfm) => {
|
|
68
|
-
const targets = assets.targets ? Object.keys(assets.targets) : [];
|
|
69
|
-
if (targets.includes(tfm))
|
|
70
|
-
return tfm;
|
|
71
|
-
return targets.find((k) => k.startsWith(`${tfm}/`));
|
|
72
|
-
};
|
|
73
|
-
const collectCompileDlls = (assets, targetKey, packageFolder) => {
|
|
74
|
-
const targets = assets.targets?.[targetKey];
|
|
75
|
-
const libraries = assets.libraries ?? {};
|
|
76
|
-
const byLibrary = new Map();
|
|
77
|
-
if (!targets || typeof targets !== "object")
|
|
78
|
-
return byLibrary;
|
|
79
|
-
for (const [libKey, libValue] of Object.entries(targets)) {
|
|
80
|
-
if (!libKey || !libValue || typeof libValue !== "object")
|
|
81
|
-
continue;
|
|
82
|
-
const libInfo = libraries[libKey];
|
|
83
|
-
if (!libInfo || libInfo.type !== "package" || !libInfo.path)
|
|
84
|
-
continue;
|
|
85
|
-
const compile = libValue.compile;
|
|
86
|
-
if (!compile || typeof compile !== "object")
|
|
87
|
-
continue;
|
|
88
|
-
const dlls = Object.keys(compile)
|
|
89
|
-
.filter((p) => p.toLowerCase().endsWith(".dll"))
|
|
90
|
-
.map((p) => join(packageFolder, libInfo.path, p));
|
|
91
|
-
if (dlls.length > 0)
|
|
92
|
-
byLibrary.set(libKey, dlls);
|
|
28
|
+
if (!ver.trim()) {
|
|
29
|
+
return { ok: false, error: "NuGet version must be non-empty" };
|
|
93
30
|
}
|
|
94
|
-
return byLibrary;
|
|
95
|
-
};
|
|
96
|
-
export const addNugetCommand = (packageId, version, typesPackage, projectRoot, options = {}) => {
|
|
97
|
-
const id = packageId.trim();
|
|
98
|
-
const ver = version.trim();
|
|
99
|
-
if (!id)
|
|
100
|
-
return { ok: false, error: "PackageId must be non-empty" };
|
|
101
|
-
if (!ver)
|
|
102
|
-
return { ok: false, error: "Version must be non-empty" };
|
|
103
31
|
if (typesPackage !== undefined && !isValidTypesPackageName(typesPackage)) {
|
|
104
32
|
return { ok: false, error: `Invalid types package name: ${typesPackage}` };
|
|
105
33
|
}
|
|
106
|
-
const tsonicConfigResult =
|
|
34
|
+
const tsonicConfigResult = loadConfig(configPath);
|
|
107
35
|
if (!tsonicConfigResult.ok)
|
|
108
36
|
return tsonicConfigResult;
|
|
109
|
-
const
|
|
37
|
+
const config = tsonicConfigResult.value;
|
|
110
38
|
const dotnet = config.dotnet ?? {};
|
|
111
|
-
const existing = [
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
39
|
+
const existing = [
|
|
40
|
+
...(dotnet.packageReferences ?? []),
|
|
41
|
+
];
|
|
42
|
+
const idx = existing.findIndex((p) => normalizePkgId(p.id) === normalizePkgId(id));
|
|
43
|
+
if (idx >= 0) {
|
|
44
|
+
const current = existing[idx];
|
|
45
|
+
if (current?.version !== ver) {
|
|
115
46
|
return {
|
|
116
47
|
ok: false,
|
|
117
|
-
error: `NuGet package already present with a different version: ${
|
|
48
|
+
error: `NuGet package already present with a different version: ${current?.id} ${current?.version}\n` +
|
|
118
49
|
`Refusing to change versions automatically (airplane-grade). Update tsonic.json manually if intended.`,
|
|
119
50
|
};
|
|
120
51
|
}
|
|
52
|
+
if (typesPackage) {
|
|
53
|
+
if (current?.types && current.types !== typesPackage) {
|
|
54
|
+
return {
|
|
55
|
+
ok: false,
|
|
56
|
+
error: `NuGet package already present with a different types package:\n` +
|
|
57
|
+
`- ${current.id} ${current.version}\n` +
|
|
58
|
+
`- existing: ${current.types}\n` +
|
|
59
|
+
`- requested: ${typesPackage}\n` +
|
|
60
|
+
`Refusing to change automatically (airplane-grade). Update tsonic.json manually if intended.`,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
existing[idx] = { ...current, types: typesPackage };
|
|
64
|
+
}
|
|
121
65
|
}
|
|
122
66
|
else {
|
|
123
|
-
existing.push({ id, version: ver });
|
|
67
|
+
existing.push(typesPackage ? { id, version: ver, types: typesPackage } : { id, version: ver });
|
|
124
68
|
}
|
|
125
69
|
const nextConfig = {
|
|
126
70
|
...config,
|
|
@@ -132,125 +76,19 @@ export const addNugetCommand = (packageId, version, typesPackage, projectRoot, o
|
|
|
132
76
|
const writeResult = writeTsonicJson(configPath, nextConfig);
|
|
133
77
|
if (!writeResult.ok)
|
|
134
78
|
return writeResult;
|
|
79
|
+
const bindings = typesPackage ??
|
|
80
|
+
existing.find((p) => normalizePkgId(p.id) === normalizePkgId(id))?.types ??
|
|
81
|
+
defaultBindingsPackageNameForNuget(id);
|
|
135
82
|
if (typesPackage) {
|
|
136
83
|
const installResult = npmInstallDevDependency(projectRoot, typesPackage, options);
|
|
137
84
|
if (!installResult.ok)
|
|
138
85
|
return installResult;
|
|
139
|
-
return {
|
|
140
|
-
ok: true,
|
|
141
|
-
value: { packageId: id, version: ver, bindings: typesPackage },
|
|
142
|
-
};
|
|
86
|
+
return { ok: true, value: { packageId: id, version: ver, bindings } };
|
|
143
87
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const coreLib = join(projectRoot, "node_modules/@tsonic/core");
|
|
150
|
-
if (!existsSync(join(dotnetLib, "package.json"))) {
|
|
151
|
-
return {
|
|
152
|
-
ok: false,
|
|
153
|
-
error: "Missing @tsonic/dotnet in node_modules. Run 'tsonic project init' (recommended) or install it manually.",
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
if (!existsSync(join(coreLib, "package.json"))) {
|
|
157
|
-
return {
|
|
158
|
-
ok: false,
|
|
159
|
-
error: "Missing @tsonic/core in node_modules. Run 'tsonic project init' (recommended) or install it manually.",
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
const targetFramework = nextConfig.dotnetVersion ?? "net10.0";
|
|
163
|
-
const restoreDir = join(projectRoot, ".tsonic", "nuget");
|
|
164
|
-
const restoreProject = writeRestoreProject(restoreDir, targetFramework, existing);
|
|
165
|
-
if (!restoreProject.ok)
|
|
166
|
-
return restoreProject;
|
|
167
|
-
const assetsPathResult = dotnetRestore(restoreProject.value, options);
|
|
168
|
-
if (!assetsPathResult.ok)
|
|
169
|
-
return assetsPathResult;
|
|
170
|
-
let assets;
|
|
171
|
-
try {
|
|
172
|
-
assets = JSON.parse(readFileSync(assetsPathResult.value, "utf-8"));
|
|
173
|
-
}
|
|
174
|
-
catch (error) {
|
|
175
|
-
return {
|
|
176
|
-
ok: false,
|
|
177
|
-
error: `Failed to parse project.assets.json: ${error instanceof Error ? error.message : String(error)}`,
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
const packageFolder = pickPackageFolder(assets);
|
|
181
|
-
if (!packageFolder) {
|
|
182
|
-
return { ok: false, error: "project.assets.json missing packageFolders (unexpected)" };
|
|
183
|
-
}
|
|
184
|
-
const targetKey = findTargetKey(assets, targetFramework);
|
|
185
|
-
if (!targetKey) {
|
|
186
|
-
const available = assets.targets ? Object.keys(assets.targets).join("\n") : "(none)";
|
|
187
|
-
return {
|
|
188
|
-
ok: false,
|
|
189
|
-
error: `No restore target found for ${targetFramework}. Available targets:\n${available}`,
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
const dllsByLibrary = collectCompileDlls(assets, targetKey, packageFolder);
|
|
193
|
-
const rootLibKey = Array.from(dllsByLibrary.keys()).find((k) => k.toLowerCase().startsWith(`${normalizePkgId(id)}/`));
|
|
194
|
-
if (!rootLibKey) {
|
|
195
|
-
return {
|
|
196
|
-
ok: false,
|
|
197
|
-
error: `No compile-time assemblies found for ${id} ${ver}.\n` +
|
|
198
|
-
`This may be a meta-package or incompatible target framework (${targetFramework}).`,
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
const seedDlls = dllsByLibrary.get(rootLibKey) ?? [];
|
|
202
|
-
if (seedDlls.length === 0) {
|
|
203
|
-
return {
|
|
204
|
-
ok: false,
|
|
205
|
-
error: `No .dll compile assets found for ${id} ${ver} under ${targetFramework}.`,
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
const compileDirs = new Set();
|
|
209
|
-
for (const dlls of dllsByLibrary.values()) {
|
|
210
|
-
for (const dll of dlls)
|
|
211
|
-
compileDirs.add(dirname(dll));
|
|
212
|
-
}
|
|
213
|
-
const runtimesResult = listDotnetRuntimes(projectRoot);
|
|
214
|
-
if (!runtimesResult.ok)
|
|
215
|
-
return runtimesResult;
|
|
216
|
-
const runtimes = runtimesResult.value;
|
|
217
|
-
const naming = detectTsbindgenNaming(nextConfig);
|
|
218
|
-
const generatedPackage = defaultBindingsPackageNameForNuget(id);
|
|
219
|
-
const bindingsDir = join(projectRoot, "bindings", generatedPackage);
|
|
220
|
-
mkdirSync(bindingsDir, { recursive: true });
|
|
221
|
-
const packageJsonResult = ensurePackageJson(bindingsDir, generatedPackage);
|
|
222
|
-
if (!packageJsonResult.ok)
|
|
223
|
-
return packageJsonResult;
|
|
224
|
-
const generateArgs = [
|
|
225
|
-
...seedDlls.flatMap((p) => ["-a", p]),
|
|
226
|
-
"-o",
|
|
227
|
-
bindingsDir,
|
|
228
|
-
"--naming",
|
|
229
|
-
naming,
|
|
230
|
-
"--lib",
|
|
231
|
-
dotnetLib,
|
|
232
|
-
"--lib",
|
|
233
|
-
coreLib,
|
|
234
|
-
];
|
|
235
|
-
for (const rt of runtimes)
|
|
236
|
-
generateArgs.push("--ref-dir", rt.dir);
|
|
237
|
-
for (const d of compileDirs)
|
|
238
|
-
generateArgs.push("--ref-dir", d);
|
|
239
|
-
for (const dep of options.deps ?? []) {
|
|
240
|
-
generateArgs.push("--ref-dir", resolveFromProjectRoot(projectRoot, dep));
|
|
241
|
-
}
|
|
242
|
-
const genResult = tsbindgenGenerate(projectRoot, tsbindgenDll, generateArgs, options);
|
|
243
|
-
if (!genResult.ok)
|
|
244
|
-
return genResult;
|
|
245
|
-
const ensurePkg = ensurePackageJson(bindingsDir, generatedPackage);
|
|
246
|
-
if (!ensurePkg.ok)
|
|
247
|
-
return ensurePkg;
|
|
248
|
-
const installLocal = npmInstallDevDependency(projectRoot, `file:bindings/${generatedPackage}`, options);
|
|
249
|
-
if (!installLocal.ok)
|
|
250
|
-
return installLocal;
|
|
251
|
-
return {
|
|
252
|
-
ok: true,
|
|
253
|
-
value: { packageId: id, version: ver, bindings: generatedPackage },
|
|
254
|
-
};
|
|
88
|
+
// Auto-generate bindings for all auto-gen dependencies (including transitive deps).
|
|
89
|
+
const restoreResult = restoreCommand(configPath, options);
|
|
90
|
+
if (!restoreResult.ok)
|
|
91
|
+
return restoreResult;
|
|
92
|
+
return { ok: true, value: { packageId: id, version: ver, bindings } };
|
|
255
93
|
};
|
|
256
94
|
//# sourceMappingURL=add-nuget.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"add-nuget.js","sourceRoot":"","sources":["../../src/commands/add-nuget.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"add-nuget.js","sourceRoot":"","sources":["../../src/commands/add-nuget.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import type { Result } from "../types.js";
|
|
13
13
|
import { type AddCommandOptions } from "./add-common.js";
|
|
14
14
|
export type AddPackageOptions = AddCommandOptions;
|
|
15
|
-
export declare const addPackageCommand: (dllPath: string, typesPackage: string | undefined,
|
|
15
|
+
export declare const addPackageCommand: (dllPath: string, typesPackage: string | undefined, configPath: string, options?: AddPackageOptions) => Result<{
|
|
16
16
|
dllsCopied: number;
|
|
17
17
|
bindings: string;
|
|
18
18
|
}, string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"add-package.d.ts","sourceRoot":"","sources":["../../src/commands/add-package.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAUH,OAAO,KAAK,EAAE,MAAM,EAAgB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"add-package.d.ts","sourceRoot":"","sources":["../../src/commands/add-package.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAUH,OAAO,KAAK,EAAE,MAAM,EAAgB,MAAM,aAAa,CAAC;AAGxD,OAAO,EAaL,KAAK,iBAAiB,EAEvB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,MAAM,iBAAiB,GAAG,iBAAiB,CAAC;AAqClD,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,EACf,cAAc,MAAM,GAAG,SAAS,EAChC,YAAY,MAAM,EAClB,UAAS,iBAAsB,KAC9B,MAAM,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EAAE,MAAM,CAiTzD,CAAC"}
|
|
@@ -10,9 +10,11 @@
|
|
|
10
10
|
* - Any unresolved dependency is a hard failure with actionable diagnostics
|
|
11
11
|
*/
|
|
12
12
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, } from "node:fs";
|
|
13
|
-
import { basename, join } from "node:path";
|
|
13
|
+
import { basename, dirname, join } from "node:path";
|
|
14
14
|
import { createHash } from "node:crypto";
|
|
15
|
-
import {
|
|
15
|
+
import { loadConfig } from "../config.js";
|
|
16
|
+
import { isBuiltInRuntimeDllPath } from "../dotnet/runtime-dlls.js";
|
|
17
|
+
import { bindingsStoreDir, defaultBindingsPackageNameForDll, detectTsbindgenNaming, ensureGeneratedBindingsPackageJson, installGeneratedBindingsPackage, listDotnetRuntimes, npmInstallDevDependency, resolveFromProjectRoot, resolvePackageRoot, resolveTsbindgenDllPath, tsbindgenGenerate, tsbindgenResolveClosure, writeTsonicJson, } from "./add-common.js";
|
|
16
18
|
const sha256File = (path) => {
|
|
17
19
|
const data = readFileSync(path);
|
|
18
20
|
// crypto.update's types are stricter than Buffer's ArrayBufferLike surface.
|
|
@@ -23,6 +25,7 @@ const addUnique = (arr, value) => {
|
|
|
23
25
|
if (!arr.includes(value))
|
|
24
26
|
arr.push(value);
|
|
25
27
|
};
|
|
28
|
+
const hasFrameworkReference = (arr, value) => arr.some((r) => (typeof r === "string" ? r : r.id).toLowerCase() === value.toLowerCase());
|
|
26
29
|
const isValidTypesPackageName = (name) => {
|
|
27
30
|
if (!name.startsWith("@") && !name.includes("/"))
|
|
28
31
|
return true;
|
|
@@ -34,7 +37,8 @@ const pathIsWithin = (path, dir) => {
|
|
|
34
37
|
const normalizedBase = normalizedDir.replace(/\\/g, "/");
|
|
35
38
|
return normalizedPath.startsWith(normalizedBase);
|
|
36
39
|
};
|
|
37
|
-
export const addPackageCommand = (dllPath, typesPackage,
|
|
40
|
+
export const addPackageCommand = (dllPath, typesPackage, configPath, options = {}) => {
|
|
41
|
+
const projectRoot = dirname(configPath);
|
|
38
42
|
const dllAbs = resolveFromProjectRoot(projectRoot, dllPath);
|
|
39
43
|
if (!existsSync(dllAbs)) {
|
|
40
44
|
return { ok: false, error: `DLL not found: ${dllAbs}` };
|
|
@@ -45,13 +49,20 @@ export const addPackageCommand = (dllPath, typesPackage, projectRoot, options =
|
|
|
45
49
|
error: `Invalid DLL path: ${dllAbs} (must end with .dll)`,
|
|
46
50
|
};
|
|
47
51
|
}
|
|
52
|
+
if (isBuiltInRuntimeDllPath(dllAbs)) {
|
|
53
|
+
return {
|
|
54
|
+
ok: false,
|
|
55
|
+
error: `Refusing to add built-in runtime DLL: ${basename(dllAbs)}\n` +
|
|
56
|
+
`Tsonic includes its runtime automatically. Remove it from dotnet.libraries and try again.`,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
48
59
|
if (typesPackage !== undefined && !isValidTypesPackageName(typesPackage)) {
|
|
49
60
|
return { ok: false, error: `Invalid types package name: ${typesPackage}` };
|
|
50
61
|
}
|
|
51
|
-
const tsonicConfigResult =
|
|
62
|
+
const tsonicConfigResult = loadConfig(configPath);
|
|
52
63
|
if (!tsonicConfigResult.ok)
|
|
53
64
|
return tsonicConfigResult;
|
|
54
|
-
const
|
|
65
|
+
const config = tsonicConfigResult.value;
|
|
55
66
|
const tsbindgenDllResult = resolveTsbindgenDllPath(projectRoot);
|
|
56
67
|
if (!tsbindgenDllResult.ok)
|
|
57
68
|
return tsbindgenDllResult;
|
|
@@ -119,9 +130,13 @@ export const addPackageCommand = (dllPath, typesPackage, projectRoot, options =
|
|
|
119
130
|
for (const rel of copiedRelPaths) {
|
|
120
131
|
addUnique(libraries, rel);
|
|
121
132
|
}
|
|
122
|
-
const frameworkRefs = [
|
|
133
|
+
const frameworkRefs = [
|
|
134
|
+
...(dotnet.frameworkReferences ?? []),
|
|
135
|
+
];
|
|
123
136
|
for (const fr of requiredFrameworkRefs) {
|
|
124
|
-
|
|
137
|
+
if (!hasFrameworkReference(frameworkRefs, fr)) {
|
|
138
|
+
frameworkRefs.push(fr);
|
|
139
|
+
}
|
|
125
140
|
}
|
|
126
141
|
const nextConfig = {
|
|
127
142
|
...config,
|
|
@@ -145,64 +160,153 @@ export const addPackageCommand = (dllPath, typesPackage, projectRoot, options =
|
|
|
145
160
|
};
|
|
146
161
|
}
|
|
147
162
|
const naming = detectTsbindgenNaming(nextConfig);
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
163
|
+
const dotnetRoot = resolvePackageRoot(projectRoot, "@tsonic/dotnet");
|
|
164
|
+
if (!dotnetRoot.ok)
|
|
165
|
+
return dotnetRoot;
|
|
166
|
+
const coreRoot = resolvePackageRoot(projectRoot, "@tsonic/core");
|
|
167
|
+
if (!coreRoot.ok)
|
|
168
|
+
return coreRoot;
|
|
169
|
+
const dotnetLib = dotnetRoot.value;
|
|
170
|
+
const coreLib = coreRoot.value;
|
|
171
|
+
const nonFramework = closure.resolvedAssemblies.filter((asm) => {
|
|
172
|
+
const runtimeDir = runtimeDirs.find((rt) => pathIsWithin(asm.path, rt.dir));
|
|
173
|
+
return !runtimeDir;
|
|
174
|
+
});
|
|
175
|
+
const identityKey = (asm) => `${asm.name}|${asm.publicKeyToken}|${asm.culture}`;
|
|
176
|
+
const byId = new Map();
|
|
177
|
+
const destPathById = new Map();
|
|
178
|
+
const directDeps = new Map();
|
|
179
|
+
for (const asm of nonFramework) {
|
|
180
|
+
const id = identityKey(asm);
|
|
181
|
+
if (byId.has(id)) {
|
|
182
|
+
return {
|
|
183
|
+
ok: false,
|
|
184
|
+
error: `Ambiguous assembly identity in closure: ${asm.name} (${asm.publicKeyToken}, ${asm.culture}).\n` +
|
|
185
|
+
`This indicates multiple assemblies with the same identity were resolved, which is not supported.`,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
byId.set(id, asm);
|
|
189
|
+
const destPath = join(libDir, basename(asm.path));
|
|
190
|
+
if (!existsSync(destPath)) {
|
|
191
|
+
return {
|
|
192
|
+
ok: false,
|
|
193
|
+
error: `Internal error: expected copied DLL to exist: ${destPath}`,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
destPathById.set(id, destPath);
|
|
160
197
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
198
|
+
const ids = new Set(byId.keys());
|
|
199
|
+
for (const asm of nonFramework) {
|
|
200
|
+
const id = identityKey(asm);
|
|
201
|
+
const refs = asm.references ?? [];
|
|
202
|
+
const deps = [];
|
|
203
|
+
for (const r of refs) {
|
|
204
|
+
const depId = `${r.name}|${r.publicKeyToken}|${r.culture}`;
|
|
205
|
+
if (ids.has(depId))
|
|
206
|
+
deps.push(depId);
|
|
207
|
+
}
|
|
208
|
+
// De-dup while preserving order.
|
|
209
|
+
directDeps.set(id, Array.from(new Set(deps)));
|
|
166
210
|
}
|
|
167
|
-
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
211
|
+
// Topological order (deps first).
|
|
212
|
+
const order = [];
|
|
213
|
+
const state = new Map();
|
|
214
|
+
const visit = (id) => {
|
|
215
|
+
const s = state.get(id);
|
|
216
|
+
if (s === "done")
|
|
217
|
+
return { ok: true, value: undefined };
|
|
218
|
+
if (s === "visiting") {
|
|
219
|
+
return { ok: false, error: `Cycle detected in DLL dependency graph at: ${id}` };
|
|
220
|
+
}
|
|
221
|
+
state.set(id, "visiting");
|
|
222
|
+
for (const dep of directDeps.get(id) ?? []) {
|
|
223
|
+
const r = visit(dep);
|
|
224
|
+
if (!r.ok)
|
|
225
|
+
return r;
|
|
226
|
+
}
|
|
227
|
+
state.set(id, "done");
|
|
228
|
+
order.push(id);
|
|
229
|
+
return { ok: true, value: undefined };
|
|
230
|
+
};
|
|
231
|
+
for (const id of ids) {
|
|
232
|
+
const r = visit(id);
|
|
233
|
+
if (!r.ok)
|
|
234
|
+
return r;
|
|
174
235
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
for (const dir of runtimeDirs) {
|
|
188
|
-
generateArgs.push("--ref-dir", dir.dir);
|
|
236
|
+
// Compute transitive deps for --lib.
|
|
237
|
+
const transitiveDeps = new Map();
|
|
238
|
+
for (const id of order) {
|
|
239
|
+
const set = new Set();
|
|
240
|
+
for (const dep of directDeps.get(id) ?? []) {
|
|
241
|
+
set.add(dep);
|
|
242
|
+
const depTrans = transitiveDeps.get(dep);
|
|
243
|
+
if (depTrans)
|
|
244
|
+
for (const t of depTrans)
|
|
245
|
+
set.add(t);
|
|
246
|
+
}
|
|
247
|
+
transitiveDeps.set(id, set);
|
|
189
248
|
}
|
|
190
|
-
|
|
191
|
-
|
|
249
|
+
const bindingsDirById = new Map();
|
|
250
|
+
const packageNameById = new Map();
|
|
251
|
+
for (const id of order) {
|
|
252
|
+
const asm = byId.get(id);
|
|
253
|
+
const destPath = destPathById.get(id);
|
|
254
|
+
if (!asm || !destPath) {
|
|
255
|
+
return { ok: false, error: `Internal error: missing assembly info for ${id}` };
|
|
256
|
+
}
|
|
257
|
+
const packageName = defaultBindingsPackageNameForDll(destPath);
|
|
258
|
+
const bindingsDir = bindingsStoreDir(projectRoot, "dll", packageName);
|
|
259
|
+
bindingsDirById.set(id, bindingsDir);
|
|
260
|
+
packageNameById.set(id, packageName);
|
|
261
|
+
const pkgJsonResult = ensureGeneratedBindingsPackageJson(bindingsDir, packageName, {
|
|
262
|
+
kind: "dll",
|
|
263
|
+
source: {
|
|
264
|
+
assemblyName: asm.name,
|
|
265
|
+
version: asm.version,
|
|
266
|
+
path: `lib/${basename(destPath)}`,
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
if (!pkgJsonResult.ok)
|
|
270
|
+
return pkgJsonResult;
|
|
271
|
+
const generateArgs = [
|
|
272
|
+
"-a",
|
|
273
|
+
destPath,
|
|
274
|
+
"-o",
|
|
275
|
+
bindingsDir,
|
|
276
|
+
"--naming",
|
|
277
|
+
naming,
|
|
278
|
+
"--lib",
|
|
279
|
+
dotnetLib,
|
|
280
|
+
"--lib",
|
|
281
|
+
coreLib,
|
|
282
|
+
];
|
|
283
|
+
const libs = Array.from(transitiveDeps.get(id) ?? [])
|
|
284
|
+
.map((depId) => bindingsDirById.get(depId))
|
|
285
|
+
.filter((p) => typeof p === "string");
|
|
286
|
+
libs.sort((a, b) => a.localeCompare(b));
|
|
287
|
+
for (const lib of libs)
|
|
288
|
+
generateArgs.push("--lib", lib);
|
|
289
|
+
for (const dir of runtimeDirs)
|
|
290
|
+
generateArgs.push("--ref-dir", dir.dir);
|
|
291
|
+
for (const dep of userDeps)
|
|
292
|
+
generateArgs.push("--ref-dir", dep);
|
|
293
|
+
generateArgs.push("--ref-dir", libDir);
|
|
294
|
+
const genResult = tsbindgenGenerate(projectRoot, tsbindgenDll, generateArgs, options);
|
|
295
|
+
if (!genResult.ok)
|
|
296
|
+
return genResult;
|
|
297
|
+
const installResult = installGeneratedBindingsPackage(projectRoot, packageName, bindingsDir);
|
|
298
|
+
if (!installResult.ok)
|
|
299
|
+
return installResult;
|
|
192
300
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
return ensurePkg;
|
|
200
|
-
const installLocal = npmInstallDevDependency(projectRoot, `file:bindings/${generatedPackage}`, options);
|
|
201
|
-
if (!installLocal.ok)
|
|
202
|
-
return installLocal;
|
|
301
|
+
const rootId = Array.from(order).find((id) => {
|
|
302
|
+
const dest = destPathById.get(id);
|
|
303
|
+
return dest ? basename(dest).toLowerCase() === basename(dllAbs).toLowerCase() : false;
|
|
304
|
+
});
|
|
305
|
+
const rootPackage = (rootId ? packageNameById.get(rootId) : undefined) ??
|
|
306
|
+
defaultBindingsPackageNameForDll(dllAbs);
|
|
203
307
|
return {
|
|
204
308
|
ok: true,
|
|
205
|
-
value: { dllsCopied: copiedCount, bindings:
|
|
309
|
+
value: { dllsCopied: copiedCount, bindings: rootPackage },
|
|
206
310
|
};
|
|
207
311
|
};
|
|
208
312
|
//# sourceMappingURL=add-package.js.map
|