@dbx-tools/projen 0.9.19 → 0.9.28

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.
Files changed (56) hide show
  1. package/README.md +83 -229
  2. package/index.ts +11 -18
  3. package/package.json +9 -5
  4. package/src/_task-command.ts +75 -0
  5. package/src/barrels.ts +1 -74
  6. package/src/module-exports.ts +175 -1
  7. package/src/openapi.ts +5 -72
  8. package/src/packages.ts +45 -0
  9. package/src/project-js.ts +5 -48
  10. package/src/project-predicate.ts +1 -2
  11. package/src/project-py.ts +87 -199
  12. package/src/project.ts +2 -2
  13. package/src/python-node-bindings.ts +138 -0
  14. package/src/python-node-bundle.ts +92 -0
  15. package/src/release-catalog.ts +40 -208
  16. package/src/{release-dispatch.ts → release-context.ts} +0 -11
  17. package/src/release.ts +74 -778
  18. package/src/scaffold.ts +2 -6
  19. package/src/workspace-version.ts +9 -121
  20. package/tasks/bump.ts +17 -34
  21. package/tasks/databricks-cli-assets.ts +129 -0
  22. package/tasks/local-publish.ts +61 -138
  23. package/tasks/publish-npm.ts +71 -30
  24. package/tasks/publish-python.ts +61 -148
  25. package/tasks/publish.ts +32 -182
  26. package/tasks/python-node-bindings-watch.ts +47 -0
  27. package/tasks/python-node-bindings.ts +1017 -0
  28. package/tasks/python-registry.ts +2 -9
  29. package/tasks/python-release.ts +74 -0
  30. package/tasks/release-version.ts +23 -2
  31. package/tasks/release.ts +99 -0
  32. package/tasks/sync.ts +13 -18
  33. package/tasks/version-check.ts +13 -72
  34. package/src/_rust-project.ts +0 -429
  35. package/src/_rust-release-workflow.ts +0 -698
  36. package/src/project-rs.ts +0 -1127
  37. package/src/release-plan.ts +0 -269
  38. package/src/release-please.ts +0 -242
  39. package/src/uniffi.ts +0 -65
  40. package/tasks/publish-uniffi-local.ts +0 -314
  41. package/tasks/release-assets.ts +0 -476
  42. package/tasks/release-bootstrap.ts +0 -59
  43. package/tasks/release-candidate.ts +0 -383
  44. package/tasks/release-manifest.ts +0 -273
  45. package/tasks/release-plan.ts +0 -89
  46. package/tasks/release-please.ts +0 -162
  47. package/tasks/release-pr.ts +0 -563
  48. package/tasks/release-request.ts +0 -166
  49. package/tasks/release-summary-units.ts +0 -75
  50. package/tasks/release-summary.ts +0 -381
  51. package/tasks/rust.ts +0 -178
  52. package/tasks/stamp-python.ts +0 -13
  53. package/tasks/uniffi-python.d.ts +0 -61
  54. package/tasks/uniffi-python.js +0 -191
  55. package/tasks/uniffi-release.mjs +0 -573
  56. package/tasks/uniffi.ts +0 -271
@@ -1,269 +0,0 @@
1
- /**
2
- * Deterministic affected release plan derived from manifest and graph changes.
3
- *
4
- * @module
5
- */
6
-
7
- import type {
8
- ReleaseArtifact,
9
- ReleaseProjectNode,
10
- ReleaseUnit,
11
- ReleaseUnitGraph,
12
- } from "./release-catalog.ts";
13
-
14
- /** Why a unit entered one release. */
15
- export type ReleaseReason = "direct" | "propagated";
16
-
17
- /** One independently versioned unit selected for publication. */
18
- export interface PlannedReleaseUnit {
19
- readonly id: string;
20
- readonly component: string;
21
- readonly oldVersion: string;
22
- readonly newVersion: string;
23
- readonly tag: string;
24
- readonly reason: ReleaseReason;
25
- readonly sourceHash: string;
26
- }
27
-
28
- /** One selected package publication. */
29
- export interface PlannedPackage {
30
- readonly unit: string;
31
- readonly identity: string;
32
- readonly path: string;
33
- readonly version: string;
34
- }
35
-
36
- /** One selected virtual artifact publication. */
37
- export interface PlannedArtifact extends ReleaseArtifact {
38
- readonly version: string;
39
- }
40
-
41
- /** One selected native Rust matrix row and the packages built in that row. */
42
- export interface PlannedRustTarget {
43
- readonly os: string;
44
- readonly cpu: string;
45
- readonly node: string;
46
- readonly cargo: string;
47
- readonly runner: string;
48
- readonly python: string;
49
- readonly libc: string;
50
- readonly glibcVersion: string;
51
- readonly packages: readonly string[];
52
- readonly binaries: readonly string[];
53
- readonly features: readonly string[];
54
- }
55
-
56
- /** Complete plan persisted as a workflow artifact and used for recovery. */
57
- export interface ReleasePlan {
58
- readonly schemaVersion: 2;
59
- readonly units: readonly PlannedReleaseUnit[];
60
- readonly publishBatches: readonly (readonly string[])[];
61
- readonly nodePackages: readonly PlannedPackage[];
62
- readonly pythonPackages: readonly PlannedPackage[];
63
- readonly rustPackages: readonly PlannedPackage[];
64
- readonly artifacts: readonly PlannedArtifact[];
65
- readonly rustTargets: readonly PlannedRustTarget[];
66
- readonly stages: {
67
- readonly rust: boolean;
68
- readonly python: boolean;
69
- readonly node: boolean;
70
- readonly github: boolean;
71
- readonly docs: boolean;
72
- };
73
- readonly omittedStages: readonly string[];
74
- }
75
-
76
- /** Build the affected plan from the previous and reviewed release graphs. */
77
- export function buildReleasePlan(
78
- current: ReleaseUnitGraph,
79
- previous?: ReleaseUnitGraph,
80
- ): ReleasePlan {
81
- const previousUnits = new Map(previous?.units.map((unit) => [unit.id, unit]) ?? []);
82
- const units = current.units.flatMap<PlannedReleaseUnit>((unit) => {
83
- const old = previousUnits.get(unit.id);
84
- if (!old || old.version === unit.version) return [];
85
- return [
86
- {
87
- id: unit.id,
88
- component: unit.component,
89
- oldVersion: old.version,
90
- newVersion: unit.version,
91
- tag: `${unit.component}-v${unit.version}`,
92
- reason: old.sourceHash === unit.sourceHash ? "propagated" : "direct",
93
- sourceHash: unit.sourceHash,
94
- },
95
- ];
96
- });
97
- const selected = new Set(units.map((unit) => unit.id));
98
- const versions = new Map(units.map((unit) => [unit.id, unit.newVersion]));
99
- const packagePlan = (language: ReleaseProjectNode["language"]): PlannedPackage[] =>
100
- current.projects
101
- .filter(
102
- (project) =>
103
- project.publish &&
104
- project.language === language &&
105
- project.unit !== undefined &&
106
- selected.has(project.unit),
107
- )
108
- .map((project) => ({
109
- unit: project.unit!,
110
- identity: project.identity,
111
- path: project.path,
112
- version: versions.get(project.unit!)!,
113
- }))
114
- .sort((a, b) => a.identity.localeCompare(b.identity));
115
- const artifacts = current.artifacts
116
- .filter((artifact) => artifact.publish && selected.has(artifact.unit))
117
- .map((artifact) => ({
118
- ...artifact,
119
- version: versions.get(artifact.unit)!,
120
- }));
121
- const rustPackagesByUnit = new Map<string, string[]>();
122
- for (const project of current.projects) {
123
- if (
124
- project.language === "rust" &&
125
- project.publish &&
126
- project.unit &&
127
- selected.has(project.unit)
128
- ) {
129
- rustPackagesByUnit.set(project.unit, [
130
- ...(rustPackagesByUnit.get(project.unit) ?? []),
131
- project.identity,
132
- ]);
133
- }
134
- }
135
- const rustTargets = new Map<string, PlannedRustTarget>();
136
- for (const artifact of artifacts) {
137
- const targets = artifact.data?.targets;
138
- if (!Array.isArray(targets)) continue;
139
- const binary = artifact.kind === "github-binary";
140
- for (const value of targets) {
141
- if (!value || typeof value !== "object" || Array.isArray(value)) continue;
142
- const target = value as Record<string, unknown>;
143
- const required = ["os", "cpu", "node", "cargo", "runner", "python"] as const;
144
- if (!required.every((field) => typeof target[field] === "string")) continue;
145
- const key = String(target.node);
146
- const currentTarget = rustTargets.get(key);
147
- const packages = [
148
- ...new Set([
149
- ...(currentTarget?.packages ?? []),
150
- ...(rustPackagesByUnit.get(artifact.unit) ?? []),
151
- ]),
152
- ].sort();
153
- const binaries = binary
154
- ? [
155
- ...new Set([
156
- ...(currentTarget?.binaries ?? []),
157
- ...(typeof artifact.data?.binary === "string" ? [artifact.data.binary] : []),
158
- ]),
159
- ].sort()
160
- : (currentTarget?.binaries ?? []);
161
- const configuredFeatures = Array.isArray(artifact.data?.features)
162
- ? artifact.data.features.filter((feature): feature is string => typeof feature === "string")
163
- : [];
164
- const crate = typeof artifact.data?.crate === "string" ? artifact.data.crate : undefined;
165
- const features = binary
166
- ? [
167
- ...new Set([
168
- ...(currentTarget?.features ?? []),
169
- ...(crate
170
- ? configuredFeatures.map((feature) => `${crate}/${feature}`)
171
- : configuredFeatures),
172
- ]),
173
- ].sort()
174
- : [
175
- ...new Set([
176
- ...(currentTarget?.features ?? []),
177
- ...packages.map((pkg) => `${pkg}/uniffi-bindgen`),
178
- ]),
179
- ].sort();
180
- rustTargets.set(key, {
181
- os: String(target.os),
182
- cpu: String(target.cpu),
183
- node: String(target.node),
184
- cargo: String(target.cargo),
185
- runner: String(target.runner),
186
- python: String(target.python),
187
- libc: typeof target.libc === "string" ? target.libc : "",
188
- glibcVersion: typeof target.glibcVersion === "string" ? target.glibcVersion : "",
189
- packages,
190
- binaries,
191
- features,
192
- });
193
- }
194
- }
195
- const plannedRustTargets = [...rustTargets.values()].sort((a, b) => a.node.localeCompare(b.node));
196
- const publishBatches = current.publishBatches
197
- .map((batch) => batch.filter((unit) => selected.has(unit)))
198
- .filter((batch) => batch.length > 0);
199
- const nodePackages = packagePlan("javascript");
200
- const pythonPackages = packagePlan("python");
201
- const rustPackages = packagePlan("rust");
202
- const stages = {
203
- rust:
204
- rustPackages.length > 0 ||
205
- artifacts.some((artifact) => ["cargo", "github-binary"].includes(artifact.kind)),
206
- python: pythonPackages.length > 0 || artifacts.some((artifact) => artifact.kind === "pypi"),
207
- node: nodePackages.length > 0 || artifacts.some((artifact) => artifact.kind === "npm"),
208
- github: artifacts.some((artifact) => artifact.kind === "github-binary"),
209
- docs: units.length > 0 || artifacts.some((artifact) => artifact.kind === "documentation"),
210
- };
211
- const omittedStages = Object.entries(stages)
212
- .filter(([, enabled]) => !enabled)
213
- .map(([name]) => name);
214
- return {
215
- schemaVersion: 2,
216
- units,
217
- publishBatches,
218
- nodePackages,
219
- pythonPackages,
220
- rustPackages,
221
- artifacts,
222
- rustTargets: plannedRustTargets,
223
- stages,
224
- omittedStages,
225
- };
226
- }
227
-
228
- /** Reconstruct a one-component plan for an idempotent manual recovery. */
229
- export function buildRecoveryReleasePlan(
230
- current: ReleaseUnitGraph,
231
- component: string,
232
- version: string,
233
- ): ReleasePlan {
234
- const unit = current.units.find(
235
- (candidate) => candidate.component === component || candidate.id === component,
236
- );
237
- if (!unit) throw new Error(`Unknown recovery component ${component}`);
238
- if (unit.version !== version) {
239
- throw new Error(`Recovery version ${version} does not match ${unit.component}@${unit.version}`);
240
- }
241
- const previous: ReleaseUnitGraph = {
242
- ...current,
243
- units: current.units.map((candidate) =>
244
- candidate.id === unit.id
245
- ? { ...candidate, version: "0.0.0", sourceHash: candidate.sourceHash }
246
- : candidate,
247
- ),
248
- };
249
- return buildReleasePlan(current, previous);
250
- }
251
-
252
- /** Validate that graph units and package identities are internally consistent. */
253
- export function validateReleasePlanGraph(graph: ReleaseUnitGraph): void {
254
- const units = new Map<string, ReleaseUnit>();
255
- for (const unit of graph.units) {
256
- if (units.has(unit.id)) throw new Error(`Duplicate release plan unit ${unit.id}`);
257
- units.set(unit.id, unit);
258
- }
259
- for (const project of graph.projects) {
260
- if (project.unit && !units.has(project.unit)) {
261
- throw new Error(`Release project ${project.identity} has unknown unit ${project.unit}`);
262
- }
263
- }
264
- for (const artifact of graph.artifacts) {
265
- if (!units.has(artifact.unit)) {
266
- throw new Error(`Release artifact ${artifact.id} has unknown unit ${artifact.unit}`);
267
- }
268
- }
269
- }
@@ -1,242 +0,0 @@
1
- /**
2
- * Release Please integration for Projen-owned polyglot release units.
3
- *
4
- * Release Please remains responsible for semantic version selection, release
5
- * pull requests, tags, and GitHub Releases. This plugin contributes only the
6
- * dependency propagation encoded by the generated release-unit graph.
7
- *
8
- * @module
9
- */
10
-
11
- import type { Commit } from "release-please/build/src/commit.js";
12
- import type {
13
- CandidateReleasePullRequest,
14
- RepositoryConfig,
15
- } from "release-please/build/src/manifest.js";
16
- import { ManifestPlugin } from "release-please/build/src/plugin.js";
17
- import type { Release } from "release-please/build/src/release.js";
18
- import type { Scm } from "release-please/build/src/scm.js";
19
- import type { Strategy } from "release-please/build/src/strategy.js";
20
- import { CompositeUpdater } from "release-please/build/src/updaters/composite.js";
21
- import { ReleasePleaseManifest } from "release-please/build/src/updaters/release-please-manifest.js";
22
- import { BranchName } from "release-please/build/src/util/branch-name.js";
23
- import { PullRequestBody } from "release-please/build/src/util/pull-request-body.js";
24
- import { PullRequestTitle } from "release-please/build/src/util/pull-request-title.js";
25
- import { Version } from "release-please/build/src/version.js";
26
- import { PatchVersionUpdate } from "release-please/build/src/versioning-strategy.js";
27
- import { satisfies, validRange } from "semver";
28
- import type { ReleaseDependencyEdge, ReleaseUnit, ReleaseUnitGraph } from "./release-catalog.ts";
29
-
30
- /** Options for {@link ReleaseUnitWorkspacePlugin}. */
31
- export interface ReleaseUnitWorkspacePluginOptions {
32
- readonly graph: ReleaseUnitGraph;
33
- readonly manifestPath?: string;
34
- readonly merge?: boolean;
35
- }
36
-
37
- /**
38
- * Propagate Release Please candidates through the cross-language release graph.
39
- *
40
- * Direct candidates retain the semantic increment selected from their commits.
41
- * A dependent introduced only by graph propagation receives a patch.
42
- */
43
- export class ReleaseUnitWorkspacePlugin extends ManifestPlugin {
44
- private readonly graph: ReleaseUnitGraph;
45
- private readonly manifestPath: string;
46
- private readonly merge: boolean;
47
- private strategiesByPath: Record<string, Strategy> = {};
48
- private releasesByPath: Record<string, Release> = {};
49
-
50
- constructor(
51
- github: Scm,
52
- targetBranch: string,
53
- repositoryConfig: RepositoryConfig,
54
- options: ReleaseUnitWorkspacePluginOptions,
55
- ) {
56
- super(github, targetBranch, repositoryConfig);
57
- this.graph = options.graph;
58
- this.manifestPath = options.manifestPath ?? ".release-please-manifest.json";
59
- this.merge = options.merge ?? true;
60
- }
61
-
62
- public override async run(
63
- candidates: CandidateReleasePullRequest[],
64
- ): Promise<CandidateReleasePullRequest[]> {
65
- const unitsById = new Map(this.graph.units.map((unit) => [unit.id, unit]));
66
- const unitsByPath = new Map(this.graph.units.map((unit) => [releaseUnitPath(unit), unit]));
67
- const inScope: CandidateReleasePullRequest[] = [];
68
- const outOfScope: CandidateReleasePullRequest[] = [];
69
- const versions = new Map<string, Version>();
70
- const direct = new Set<string>();
71
-
72
- for (const candidate of candidates) {
73
- const unit = unitsByPath.get(candidate.path);
74
- if (!unit || !candidate.pullRequest.version) {
75
- outOfScope.push(candidate);
76
- continue;
77
- }
78
- inScope.push(candidate);
79
- direct.add(unit.id);
80
- versions.set(unit.id, candidate.pullRequest.version);
81
- }
82
- if (inScope.length === 0) return candidates;
83
-
84
- const queue = [...direct];
85
- const propagated = new Set<string>();
86
- while (queue.length > 0) {
87
- const changed = queue.shift()!;
88
- const changedVersion = versions.get(changed);
89
- if (!changedVersion) continue;
90
- for (const edge of this.graph.edges.filter((candidate) => candidate.to === changed)) {
91
- if (!shouldPropagate(edge, changedVersion, unitsById.get(changed))) continue;
92
- if (versions.has(edge.from)) continue;
93
- const dependent = unitsById.get(edge.from);
94
- if (!dependent) throw new Error(`Release graph references unknown unit ${edge.from}`);
95
- versions.set(dependent.id, new PatchVersionUpdate().bump(Version.parse(dependent.version)));
96
- propagated.add(dependent.id);
97
- queue.push(dependent.id);
98
- }
99
- }
100
-
101
- for (const id of propagated) {
102
- const unit = unitsById.get(id)!;
103
- inScope.push(await this.newCandidate(unit, versions.get(id)!));
104
- }
105
-
106
- const merged = this.merge ? mergeCandidates(inScope, this.targetBranch) : inScope;
107
- const manifestVersions = new Map(
108
- [...versions].map(([id, version]) => {
109
- const unit = unitsById.get(id);
110
- if (!unit) throw new Error(`Release version resolved for unknown unit ${id}`);
111
- return [releaseUnitPath(unit), version] as const;
112
- }),
113
- );
114
- const first = merged[0];
115
- if (first) {
116
- const manifestVersion = versions.values().next().value;
117
- if (!manifestVersion) throw new Error("Release Please resolved no component versions");
118
- const updater = new ReleasePleaseManifest({
119
- version: manifestVersion,
120
- versionsMap: manifestVersions,
121
- });
122
- const existing = first.pullRequest.updates.find(
123
- (update) => update.path === this.manifestPath,
124
- );
125
- if (existing) {
126
- existing.updater = new CompositeUpdater(existing.updater, updater);
127
- } else {
128
- first.pullRequest.updates.push({
129
- path: this.manifestPath,
130
- createIfMissing: false,
131
- updater,
132
- });
133
- }
134
- }
135
- return [...outOfScope, ...merged];
136
- }
137
-
138
- public override async preconfigure(
139
- strategiesByPath: Record<string, Strategy>,
140
- _commitsByPath: Record<string, Commit[]>,
141
- releasesByPath: Record<string, Release>,
142
- ): Promise<Record<string, Strategy>> {
143
- this.strategiesByPath = strategiesByPath;
144
- this.releasesByPath = releasesByPath;
145
- return strategiesByPath;
146
- }
147
-
148
- private async newCandidate(
149
- unit: ReleaseUnit,
150
- version: Version,
151
- ): Promise<CandidateReleasePullRequest> {
152
- const path = releaseUnitPath(unit);
153
- const strategy = this.strategiesByPath[path];
154
- if (!strategy) throw new Error(`Release Please strategy not found for ${path}`);
155
- const pullRequest = await strategy.buildReleasePullRequest(
156
- [],
157
- this.releasesByPath[path],
158
- false,
159
- [],
160
- { newVersion: version },
161
- );
162
- if (!pullRequest) throw new Error(`Release Please did not build a candidate for ${unit.id}`);
163
- return {
164
- path,
165
- pullRequest,
166
- config: this.repositoryConfig[path] ?? { releaseType: "simple" },
167
- };
168
- }
169
- }
170
-
171
- /** Attach graph propagation to a Release Please manifest instance. */
172
- export function addReleaseUnitPlugin(
173
- plugins: ManifestPlugin[],
174
- github: Scm,
175
- targetBranch: string,
176
- repositoryConfig: RepositoryConfig,
177
- graph: ReleaseUnitGraph,
178
- manifestPath?: string,
179
- ): void {
180
- plugins.push(
181
- new ReleaseUnitWorkspacePlugin(github, targetBranch, repositoryConfig, {
182
- graph,
183
- manifestPath,
184
- }),
185
- );
186
- }
187
-
188
- /** Stable synthetic component path consumed by Release Please. */
189
- export function releaseUnitPath(unit: Pick<ReleaseUnit, "component">): string {
190
- return `.release-units/${unit.component}`;
191
- }
192
-
193
- function shouldPropagate(
194
- edge: ReleaseDependencyEdge,
195
- newVersion: Version,
196
- dependency: ReleaseUnit | undefined,
197
- ): boolean {
198
- if (edge.propagation === "never") return false;
199
- if (edge.propagation === "always") return true;
200
- if (!edge.requirement || !dependency) return true;
201
- let requirement = edge.requirement;
202
- if (requirement.startsWith("workspace:")) {
203
- const workspaceRange = requirement.slice("workspace:".length);
204
- if (workspaceRange === "*") return true;
205
- requirement = workspaceRange ? `${workspaceRange}${dependency.version}` : dependency.version;
206
- }
207
- requirement = requirement.replace(/^==/, "=").replaceAll(",", " ");
208
- if (!validRange(requirement)) return true;
209
- return !satisfies(newVersion.toString(), requirement, { includePrerelease: true });
210
- }
211
-
212
- function mergeCandidates(
213
- candidates: CandidateReleasePullRequest[],
214
- targetBranch: string,
215
- ): CandidateReleasePullRequest[] {
216
- if (candidates.length <= 1) return candidates;
217
- const releaseData = candidates.flatMap((candidate) => candidate.pullRequest.body.releaseData);
218
- const labels = [...new Set(candidates.flatMap((candidate) => candidate.pullRequest.labels))];
219
- const rawUpdates = candidates.flatMap((candidate) => candidate.pullRequest.updates);
220
- const updatesByPath = new Map<string, (typeof rawUpdates)[number]>();
221
- for (const update of rawUpdates) {
222
- const current = updatesByPath.get(update.path);
223
- updatesByPath.set(update.path, {
224
- ...update,
225
- updater: current ? new CompositeUpdater(current.updater, update.updater) : update.updater,
226
- });
227
- }
228
- return [
229
- {
230
- path: ".",
231
- pullRequest: {
232
- title: PullRequestTitle.ofTargetBranch(targetBranch),
233
- body: new PullRequestBody(releaseData, { useComponents: true }),
234
- updates: [...updatesByPath.values()],
235
- labels,
236
- headRefName: BranchName.ofTargetBranch(targetBranch).toString(),
237
- draft: !candidates.some((candidate) => !candidate.pullRequest.draft),
238
- },
239
- config: { releaseType: "simple" },
240
- },
241
- ];
242
- }
package/src/uniffi.ts DELETED
@@ -1,65 +0,0 @@
1
- const defaultedParameters = (source: string): Map<string, Set<string>> => {
2
- const methods = new Map<string, Set<string>>();
3
- for (const match of source.matchAll(/^\s*(?:async\s+)?(\w+)\(([^)]*)\)/gm)) {
4
- const parameters = match[2]
5
- .split(",")
6
- .map((parameter) => parameter.trim())
7
- .filter((parameter) => parameter.includes("="))
8
- .map((parameter) => parameter.match(/^(\w+)\s*:/)?.[1])
9
- .filter((parameter): parameter is string => parameter !== undefined);
10
- if (parameters.length > 0) methods.set(match[1], new Set(parameters));
11
- }
12
- return methods;
13
- };
14
-
15
- export const makeDefaultedInterfaceParametersOptional = (source: string): string => {
16
- const defaults = defaultedParameters(source);
17
- return source.replace(/export interface \w+Like \{[\s\S]*?^\}/gm, (block) =>
18
- block.replace(/^(\s*)(\w+)\(([^)]*)\)/gm, (signature, indent, method, parameters) => {
19
- const defaulted = defaults.get(method);
20
- if (!defaulted) return signature;
21
- const repaired = parameters
22
- .split(",")
23
- .map((parameter: string) => {
24
- const match = parameter.match(/^(\s*)(\w+)(\s*:\s*.*)$/);
25
- if (!match || !defaulted.has(match[2])) return parameter;
26
- return `${match[1]}${match[2]}?${match[3]}`;
27
- })
28
- .join(",");
29
- return `${indent}${method}(${repaired})`;
30
- }),
31
- );
32
- };
33
-
34
- /** Remove UBRN's obsolete `*Interface` aliases once the `*Like` owner exists. */
35
- export const removeObsoleteInterfaceAliases = (source: string): string =>
36
- source.replace(
37
- /\n\/\*\*\n \* @deprecated Use `\w+Like` instead\.\n \*\/\nexport type \w+Interface = \w+Like;\n/g,
38
- "\n",
39
- );
40
-
41
- export interface TypeScriptBindingModule {
42
- readonly specifier: string;
43
- readonly source: string;
44
- }
45
-
46
- /** Add source extensions to UBRN's generated relative binding imports. */
47
- export const addTypeScriptExtensionsToBindingImports = (source: string): string =>
48
- source.replace(
49
- /(["'])\.\/_bindings(-ffi)?\1/g,
50
- (_specifier, quote: string, suffix: string | undefined) =>
51
- `${quote}./_bindings${suffix ?? ""}.ts${quote}`,
52
- );
53
-
54
- /** Add explicit type exports for interfaces that TypeScript misses through UBRN's star exports. */
55
- export const addExplicitInterfaceReexports = (
56
- facade: string,
57
- modules: readonly TypeScriptBindingModule[],
58
- ): string => {
59
- const exports = modules.flatMap(({ specifier, source }) => {
60
- const names = [...source.matchAll(/^export interface (\w+)/gm)].map((match) => match[1]!);
61
- return names.length > 0 ? [`export type { ${names.join(", ")} } from '${specifier}';`] : [];
62
- });
63
- if (exports.length === 0) return facade;
64
- return `${facade.trimEnd()}\n${exports.join("\n")}\n`;
65
- };