@dbx-tools/projen 0.9.12 → 0.9.17

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.
@@ -1248,7 +1248,7 @@ function stringify(obj, { maxDepth = 1000, numbersAsFloat = false } = {}) {
1248
1248
  */
1249
1249
 
1250
1250
  // src/_rust-release.ts
1251
- var FINGERPRINT_SCHEMA = 3;
1251
+ var FINGERPRINT_SCHEMA = 4;
1252
1252
  var VERSION_SLOT_SCHEMA = 1;
1253
1253
  var VERSION_CAPACITY = 64;
1254
1254
  var MAGIC = Buffer.from("DBXVERSION\x00\x00", "binary");
@@ -1337,9 +1337,9 @@ function workspaceVersion(root) {
1337
1337
  function toPosix(path) {
1338
1338
  return path.split(sep).join("/");
1339
1339
  }
1340
- function sourceRoots(root, metadata, sources) {
1340
+ function sourceRoots(root, metadata, sources, includeWorkspace) {
1341
1341
  const members = new Set(metadata.workspace_members);
1342
- const discovered = metadata.packages.filter((candidate) => members.has(candidate.id)).map((candidate) => dirname(candidate.manifest_path));
1342
+ const discovered = includeWorkspace ? metadata.packages.filter((candidate) => members.has(candidate.id)).map((candidate) => dirname(candidate.manifest_path)) : [];
1343
1343
  return [...new Set([...discovered, ...sources.map((source) => resolve(root, source))])].map((path) => toPosix(relative(root, path)) || ".").sort();
1344
1344
  }
1345
1345
  function ignoredInput(path) {
@@ -1397,13 +1397,13 @@ function normalizedInput(root, file, content, packageNames) {
1397
1397
  }
1398
1398
  return content;
1399
1399
  }
1400
- function sourceHash(root, sources = []) {
1400
+ function sourceHash(root, sources = [], includeWorkspace = true) {
1401
1401
  const resolvedRoot = resolve(root);
1402
1402
  const metadata = cargoMetadata(resolvedRoot);
1403
1403
  const members = new Set(metadata.workspace_members);
1404
1404
  const packageNames = new Set(metadata.packages.filter((candidate) => members.has(candidate.id)).map((candidate) => candidate.name));
1405
1405
  const hash = createHash("sha256");
1406
- for (const file of inputFiles(resolvedRoot, sourceRoots(resolvedRoot, metadata, sources))) {
1406
+ for (const file of inputFiles(resolvedRoot, sourceRoots(resolvedRoot, metadata, sources, includeWorkspace))) {
1407
1407
  const absolute = join(resolvedRoot, file);
1408
1408
  const stat = lstatSync(absolute);
1409
1409
  hash.update(file);
@@ -1426,6 +1426,7 @@ function linkerIdentity(target) {
1426
1426
  }
1427
1427
  function targetKey({
1428
1428
  rustSourceHash,
1429
+ namespace = "workspace",
1429
1430
  target,
1430
1431
  targetConfig = "",
1431
1432
  toolchain = "stable",
@@ -1435,6 +1436,7 @@ function targetKey({
1435
1436
  for (const value of [
1436
1437
  String(FINGERPRINT_SCHEMA),
1437
1438
  String(VERSION_SLOT_SCHEMA),
1439
+ namespace,
1438
1440
  rustSourceHash,
1439
1441
  target,
1440
1442
  targetConfig,
@@ -1460,12 +1462,14 @@ function fingerprint({
1460
1462
  targets,
1461
1463
  toolchain = "stable",
1462
1464
  portable = false,
1463
- sources = []
1465
+ sources = [],
1466
+ sourceOnly = false,
1467
+ namespace = "workspace"
1464
1468
  }) {
1465
1469
  if (!targets.length)
1466
1470
  throw new Error("at least one --target is required");
1467
1471
  const resolvedRoot = resolve(root);
1468
- const rustSourceHash = sourceHash(resolvedRoot, sources);
1472
+ const rustSourceHash = sourceHash(resolvedRoot, sources, !sourceOnly);
1469
1473
  const rustc = portable ? undefined : rustcIdentity();
1470
1474
  const targetEntries = targets.map((specification) => {
1471
1475
  const separator = specification.indexOf("|");
@@ -1473,12 +1477,13 @@ function fingerprint({
1473
1477
  const targetConfig = separator < 0 ? "" : specification.slice(separator + 1);
1474
1478
  return [
1475
1479
  target,
1476
- targetKey({ rustSourceHash, target, targetConfig, toolchain, rustc })
1480
+ targetKey({ rustSourceHash, namespace, target, targetConfig, toolchain, rustc })
1477
1481
  ];
1478
1482
  }).sort(([left], [right]) => left.localeCompare(right));
1479
1483
  const manifest = {
1480
1484
  schemaVersion: FINGERPRINT_SCHEMA,
1481
1485
  versionSlotSchema: VERSION_SLOT_SCHEMA,
1486
+ namespace,
1482
1487
  rustSourceHash,
1483
1488
  targets: Object.fromEntries(targetEntries)
1484
1489
  };
@@ -1486,7 +1491,7 @@ function fingerprint({
1486
1491
  if (check) {
1487
1492
  const current = JSON.parse(readFileSync(outputPath, "utf8"));
1488
1493
  const targetsMatch = targetEntries.every(([target, key]) => current.targets?.[target] === key);
1489
- if (current.schemaVersion !== manifest.schemaVersion || current.versionSlotSchema !== manifest.versionSlotSchema || current.rustSourceHash !== manifest.rustSourceHash || !targetsMatch) {
1494
+ if (current.schemaVersion !== manifest.schemaVersion || current.versionSlotSchema !== manifest.versionSlotSchema || current.namespace !== manifest.namespace || current.rustSourceHash !== manifest.rustSourceHash || !targetsMatch) {
1490
1495
  throw new Error(`${outputPath} does not match current Rust inputs`);
1491
1496
  }
1492
1497
  } else {
@@ -1635,7 +1640,9 @@ function fingerprintCommand(args) {
1635
1640
  target: { type: "string", multiple: true },
1636
1641
  toolchain: { type: "string", default: "stable" },
1637
1642
  portable: { type: "boolean", default: false },
1638
- source: { type: "string", multiple: true }
1643
+ source: { type: "string", multiple: true },
1644
+ "source-only": { type: "boolean", default: false },
1645
+ namespace: { type: "string", default: "workspace" }
1639
1646
  },
1640
1647
  strict: true
1641
1648
  });
@@ -1646,7 +1653,9 @@ function fingerprintCommand(args) {
1646
1653
  targets: values.target ?? [],
1647
1654
  toolchain: values.toolchain,
1648
1655
  portable: values.portable,
1649
- sources: values.source ?? []
1656
+ sources: values.source ?? [],
1657
+ sourceOnly: values["source-only"],
1658
+ namespace: values.namespace
1650
1659
  });
1651
1660
  }
1652
1661
  function stampCommand(args) {
package/tasks/rust.ts CHANGED
@@ -3,7 +3,7 @@ import { spawnSync } from "node:child_process";
3
3
  import { existsSync } from "node:fs";
4
4
  import { dirname, isAbsolute, resolve, sep } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
- import { log } from "@dbx-tools/shared-core";
6
+ import { log, object } from "@dbx-tools/shared-core";
7
7
  import { readDbxToolsConfig, repoRoot } from "../src/packages.ts";
8
8
  import {
9
9
  discoverRustCrates,
@@ -40,7 +40,7 @@ function currentStructure(config: RustWorkspaceMapping): RustWorkspaceMapping {
40
40
  const binding = recorded.get(rust);
41
41
  return binding ? [binding] : [{ crate: "", rust }];
42
42
  });
43
- return { root: config.root, crates, bindings, binaries: config.binaries ?? [] };
43
+ return { ...config, crates, bindings };
44
44
  }
45
45
 
46
46
  /** Whether discovered crate membership or UniFFI marker membership changed. */
@@ -87,18 +87,47 @@ function generate(binding: RustBindingMapping): void {
87
87
  if (result.status !== 0) throw new Error(`binding generation exited with ${result.status}`);
88
88
  }
89
89
 
90
- export function affectedRustBindings(
90
+ function* iterateAffectedRustBindings(
91
91
  bindings: readonly RustBindingMapping[],
92
92
  changed: ReadonlySet<string>,
93
- ): RustBindingMapping[] {
93
+ ): Generator<RustBindingMapping> {
94
94
  const affected = new Set(changed);
95
- const ordered = orderRustBindings(bindings);
96
- for (const binding of ordered) {
95
+ for (const binding of orderRustBindings(bindings)) {
97
96
  if (binding.dependencies?.some((dependency) => affected.has(dependency))) {
98
97
  affected.add(binding.crate);
99
98
  }
99
+ if (affected.has(binding.crate)) yield binding;
100
100
  }
101
- return ordered.filter((binding) => affected.has(binding.crate));
101
+ }
102
+
103
+ export function affectedRustBindings(
104
+ bindings: readonly RustBindingMapping[],
105
+ changed: ReadonlySet<string>,
106
+ ): RustBindingMapping[] {
107
+ return [...iterateAffectedRustBindings(bindings, changed)];
108
+ }
109
+
110
+ /**
111
+ * Lazily resolve direct binding owners so lock checks stop at the first match.
112
+ */
113
+ export function changedRustOwners(
114
+ config: RustWorkspaceMapping,
115
+ changed: readonly string[],
116
+ ): object.Sequence<RustBindingMapping> {
117
+ return object
118
+ .sequence(changed)
119
+ .map((path) => ownerBinding(path, config.bindings))
120
+ .nonNull()
121
+ .distinct();
122
+ }
123
+
124
+ /** Collect direct owners, then lazily yield them and their dependents in order. */
125
+ export function changedRustBindings(
126
+ config: RustWorkspaceMapping,
127
+ changed: readonly string[],
128
+ ): object.Sequence<RustBindingMapping> {
129
+ const targets = new Set(changedRustOwners(config, changed).map((binding) => binding.crate));
130
+ return object.sequence(iterateAffectedRustBindings(config.bindings, targets));
102
131
  }
103
132
 
104
133
  const config = rustConfig();
@@ -113,37 +142,37 @@ async function main(): Promise<void> {
113
142
  return;
114
143
  }
115
144
 
116
- watchLoop("rust", [resolve(repoRoot, config.root)], (changed) => {
117
- const latest = rustConfig() ?? config;
118
- if (rustStructureChanged(latest)) {
119
- logger.start("Rust project structure changed - re-synthesizing (+install)");
120
- runSynth({ post: true });
121
- logger.success("Rust project structure synchronized");
122
- const refreshed = rustConfig();
123
- if (!refreshed) return;
124
- const targets = new Map<string, RustBindingMapping>();
125
- for (const path of changed) {
126
- const binding = ownerBinding(path, refreshed.bindings);
127
- if (binding) targets.set(binding.crate, binding);
145
+ watchLoop(
146
+ "rust",
147
+ [resolve(repoRoot, config.root)],
148
+ (changed) => {
149
+ const latest = rustConfig() ?? config;
150
+ if (rustStructureChanged(latest)) {
151
+ logger.start("Rust project structure changed - re-synthesizing (+install)");
152
+ runSynth({ post: true });
153
+ logger.success("Rust project structure synchronized");
154
+ const refreshed = rustConfig();
155
+ if (!refreshed) return;
156
+ for (const binding of changedRustBindings(refreshed, changed)) {
157
+ logger.start(`generating ${binding.crate} bindings`);
158
+ generate(binding);
159
+ logger.success(`generated ${binding.crate} bindings`);
160
+ }
161
+ return;
128
162
  }
129
- for (const binding of affectedRustBindings(refreshed.bindings, new Set(targets.keys()))) {
163
+ for (const binding of changedRustBindings(latest, changed)) {
130
164
  logger.start(`generating ${binding.crate} bindings`);
131
165
  generate(binding);
132
166
  logger.success(`generated ${binding.crate} bindings`);
133
167
  }
134
- return;
135
- }
136
- const targets = new Map<string, RustBindingMapping>();
137
- for (const path of changed) {
138
- const binding = ownerBinding(path, latest.bindings);
139
- if (binding) targets.set(binding.crate, binding);
140
- }
141
- for (const binding of affectedRustBindings(latest.bindings, new Set(targets.keys()))) {
142
- logger.start(`generating ${binding.crate} bindings`);
143
- generate(binding);
144
- logger.success(`generated ${binding.crate} bindings`);
145
- }
146
- });
168
+ },
169
+ {
170
+ check: (changed) => {
171
+ const latest = rustConfig() ?? config;
172
+ return changedRustOwners(latest, changed).some(() => true) || rustStructureChanged(latest);
173
+ },
174
+ },
175
+ );
147
176
  }
148
177
 
149
178
  if (import.meta.main) await main();
@@ -227,7 +227,9 @@ const facadeDependency = (name, dependency, catalog, workspaceVersions) => {
227
227
  if (dependency.startsWith("workspace:")) {
228
228
  const version = workspaceVersions.get(name);
229
229
  if (!version) throw new Error(`Missing workspace version for ${name}`);
230
- return dependency === "workspace:*" ? version : `${dependency.slice("workspace:".length)}${version}`;
230
+ return dependency === "workspace:*"
231
+ ? version
232
+ : `${dependency.slice("workspace:".length)}${version}`;
231
233
  }
232
234
  if (!dependency.startsWith("catalog:")) return dependency;
233
235
  const resolved = catalog[name];
@@ -481,6 +483,8 @@ const build = () => {
481
483
  ...(existsSync(resolve(root, "Cargo.lock")) ? ["--locked"] : []),
482
484
  "--package",
483
485
  crate,
486
+ "--features",
487
+ "uniffi-bindgen",
484
488
  "--target",
485
489
  cargoTarget,
486
490
  ]);
package/tasks/uniffi.ts CHANGED
@@ -119,6 +119,8 @@ if (!values["skip-build"]) {
119
119
  ...(existsSync(join(root, "Cargo.lock")) ? ["--locked"] : []),
120
120
  "--package",
121
121
  crate,
122
+ "--features",
123
+ "uniffi-bindgen",
122
124
  ...(values["cargo-target"] ? ["--target", values["cargo-target"]] : []),
123
125
  ]);
124
126
  }