@dbx-tools/projen 0.6.222 → 0.6.224

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/README.md CHANGED
@@ -37,6 +37,34 @@ project.synth();
37
37
  Every `src`-bearing folder under the configured roots becomes a
38
38
  `DBXToolsTypeScriptProject`. Folder path drives package name and runtime tags.
39
39
 
40
+ Use `DBXToolsTypeScriptProject` itself as the root when one package needs the
41
+ same Bun workspace, repository, task, and release defaults without a separate
42
+ non-compiling root. It preserves Projen's native compiling `tsconfig.json`
43
+ instead of creating a second owner. An explicit `outdir` is the workspace root
44
+ for naming, Git metadata, package discovery, codegen, and barrels even when the
45
+ calling process has a different current directory.
46
+
47
+ The unified dbx-tools release surface is enabled by default. Set
48
+ `releaseMode: "disabled"` to omit its workflow and bump/version/release tasks.
49
+ The inherited Projen `release` and `releaseTrigger` options are intentionally
50
+ not part of this engine's public options because they create a competing release
51
+ workflow.
52
+
53
+ Repository policy stays in the consuming `.projenrc.ts`:
54
+
55
+ - `releaseDocs` supplies repository-defined preparation and build steps plus
56
+ the Pages artifact path. The engine adds release checkout, Bun caching,
57
+ artifact upload, and deployment without naming a docs script or output tree.
58
+ - `releasePythonRoot` passes the actual Python package root to local release
59
+ preparation. Omit it when the workspace has no standard Python packages.
60
+ - `pullRequestTitlePolicy` configures semantic title types and scope policy.
61
+ Omit it or pass `false` to disable the title job.
62
+ - `workflowCacheIgnorePaths` excludes generated output trees that may contain
63
+ package manifests from the dependency-only Bun cache key.
64
+ - `extraWorkspaceMembers` declares self-synthesizing tooling packages outside
65
+ `packageRoots`. Their version, generated entrypoint, formatting, linting, and
66
+ workspace membership are derived from that declaration.
67
+
40
68
  Dependency installation runs once from this root. The default-on
41
69
  `ROOT_INSTALL_ONLY_MIXIN` clears child `install` / `install:ci` task steps during
42
70
  root pre-synthesis, including packages attached after root construction. Set
@@ -53,9 +81,9 @@ new package membership.
53
81
  without turning Python packages into JavaScript workspace projects. It generates
54
82
  the root and member `pyproject.toml` files, standard `py:*` tasks, the VS Code
55
83
  interpreter setting, and an optional manual PyPI trusted-publishing workflow.
56
- Every generated TOML file is parsed and reserialized with `smol-toml`, so nested
57
- tables and arrays use consistent formatting instead of projen's indented table
58
- headers.
84
+ Projen's native `PyprojectTomlFile` owns generated TOML formatting. Temporary
85
+ publication stamping uses parsed TOML, changes versions and sibling
86
+ dependencies structurally, and restores the original files byte-for-byte.
59
87
 
60
88
  ```ts
61
89
  import { project, projectPy } from "@dbx-tools/projen";
@@ -121,6 +149,10 @@ are library-local. Release wheels replace sibling Git requirements with matching
121
149
  native-wheel versions, and dependent publishers wait for their dependencies.
122
150
  Each crate builds its own `<crate>-uniffi-bindgen` executable so a workspace
123
151
  build has no colliding binary outputs. Packaging runs that prebuilt executable.
152
+ Cargo manifests, target config, and UniFFI config are generated from structured
153
+ Projen `TomlFile` objects. Local and release Python generation share one
154
+ dependency-free helper for generator arguments, target-specific executable
155
+ names, generated headers, empty `__init__.py`, and native-library placement.
124
156
 
125
157
  `sync --watch` runs a focused Rust watcher beside the OpenAPI watcher. Changes
126
158
  inside an existing UniFFI crate regenerate that crate and its dependent bindings
@@ -425,10 +457,12 @@ Use `--local-registry false` or `--local-pypi false` to disable either local
425
457
  publish. An explicit `--local-pypi http://localhost:3141/user/index/` overrides
426
458
  auto-detection; `--python-root` defaults to `packages/py`.
427
459
 
428
- The GitHub PR workflow runs synth plus the workspace TypeScript compile rather
429
- than the complete root build. Release preparation has already run Rust tests,
430
- workspace type-checking, and local package preflight before opening the PR.
431
- JavaScript behavior tests remain an explicit developer task.
460
+ The GitHub PR workflow runs the explicit `pr:validate` task through Projen's
461
+ public `BuildWorkflow` `buildTask` option. That task runs synth plus the
462
+ workspace TypeScript compile rather than the complete root build. Release
463
+ preparation has already run Rust tests, workspace type-checking, and local
464
+ package preflight before opening the PR. JavaScript behavior tests remain an
465
+ explicit developer task.
432
466
 
433
467
  The configured release branch publishes only when a reviewed PR changes
434
468
  `VERSION`. Its workflow creates the annotated `v*` public release boundary.
package/index.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  // Hand edits are overwritten on the next watch; this file is read-only.
4
4
 
5
5
  export const PACKAGE_IDENTIFIER = "@dbx-tools/projen";
6
- export const PACKAGE_VERSION = "0.6.222";
6
+ export const PACKAGE_VERSION = "0.6.224";
7
7
  export * as barrels from "./src/barrels.ts";
8
8
  export * as bunApp from "./src/bun-app.ts";
9
9
  export * as bunWorkflow from "./src/bun-workflow.ts";
@@ -25,6 +25,7 @@ export * as projectRs from "./src/project-rs.ts";
25
25
  export * as publish from "./src/publish.ts";
26
26
  export * as release from "./src/release.ts";
27
27
  export * as releaseDispatch from "./src/release-dispatch.ts";
28
+ export * as releaseGithub from "./src/release-github.ts";
28
29
  export * as scaffold from "./src/scaffold.ts";
29
30
  export * as tags from "./src/tags.ts";
30
31
  export * as tsconfig from "./src/tsconfig.ts";
@@ -47,7 +48,7 @@ export { PnpmWorkspaceState } from "./src/pnpm-workspace.ts";
47
48
  export type { Catalog, AllowBuilds, DBXToolsPNPMWorkspaceOptions } from "./src/pnpm-workspace.ts";
48
49
  export type { DBXToolsProjectLanguage, DBXToolsProjectOptions, DBXToolsProject, ApplyToProjectsOptions } from "./src/project.ts";
49
50
  export { PackageIdentifier, PROJEN_VERSION, DBX_TOOLS_LICENSE, DBXToolsNodeProject, ROOT_INSTALL_ONLY_MIXIN, DBXToolsTypeScriptProject } from "./src/project-js.ts";
50
- export type { DBXToolsJavaScriptProject, DBXToolsJavaScriptProjectOptions, DBXToolsTypeScriptProjectOptions } from "./src/project-js.ts";
51
+ export type { DBXToolsJavaScriptProject, DBXToolsReleaseMode, PullRequestTitlePolicyOptions, DBXToolsJavaScriptProjectOptions, DBXToolsTypeScriptProjectOptions } from "./src/project-js.ts";
51
52
  export { DBXToolsPythonProject, DBXToolsPythonWorkspace } from "./src/project-py.ts";
52
53
  export type { PythonRepositoryOptions, PythonPackageOptions, PythonTrustedPublisherOptions, DBXToolsPythonProjectOptions, PythonReleaseOptions, DBXToolsPythonWorkspaceOptions } from "./src/project-py.ts";
53
54
  export { RustReleaseOs, RustReleaseCpu, UNIFFI_RELEASE_TARGETS, DBXToolsRustProject, DBXToolsRustWorkspace } from "./src/project-rs.ts";
@@ -56,6 +57,7 @@ export { COMPILED_DIR, COMPILED_COMPILER_OPTIONS } from "./src/publish.ts";
56
57
  export { DBXToolsRelease } from "./src/release.ts";
57
58
  export type { ReleaseStage, ReleaseDocsOptions, DBXToolsReleaseOptions } from "./src/release.ts";
58
59
  export { RELEASE_TAG, RELEASE_SHA, RELEASE_VERSION } from "./src/release-dispatch.ts";
60
+ export type { GithubRepositoryIdentity } from "./src/release-github.ts";
59
61
  export { AGNOSTIC_COMPILER_OPTIONS, PACKAGE_TAG_MIXINS } from "./src/tags.ts";
60
62
  export type { PackageTag } from "./src/tags.ts";
61
63
  export { DBXToolsRootTsconfig } from "./src/tsconfig.ts";
package/package.json CHANGED
@@ -5,13 +5,13 @@
5
5
  "clobber": "projen clobber",
6
6
  "compile": "projen compile",
7
7
  "default": "projen default",
8
- "demo": "projen demo",
9
8
  "eject": "projen eject",
10
9
  "package": "projen package",
11
10
  "post-compile": "projen post-compile",
12
11
  "post-upgrade": "projen post-upgrade",
13
12
  "pre-compile": "projen pre-compile",
14
13
  "test": "projen test",
14
+ "test:external-consumer": "projen test:external-consumer",
15
15
  "upgrade": "projen upgrade",
16
16
  "watch": "projen watch",
17
17
  "projen": "projen"
@@ -25,9 +25,9 @@
25
25
  },
26
26
  "dependencies": {
27
27
  "@clack/prompts": "^1.7.0",
28
- "@dbx-tools/core": "0.6.222",
29
- "@dbx-tools/path": "0.6.222",
30
- "@dbx-tools/shared-core": "0.6.222",
28
+ "@dbx-tools/core": "0.6.224",
29
+ "@dbx-tools/path": "0.6.224",
30
+ "@dbx-tools/shared-core": "0.6.224",
31
31
  "commander": "^15.0.0",
32
32
  "concurrently": "^10.0.3",
33
33
  "constructs": "^10.6.0",
@@ -47,7 +47,7 @@
47
47
  },
48
48
  "main": "index.ts",
49
49
  "license": "Apache-2.0",
50
- "version": "0.6.222",
50
+ "version": "0.6.224",
51
51
  "types": "index.ts",
52
52
  "type": "module",
53
53
  "exports": {
package/src/barrels.ts CHANGED
@@ -63,7 +63,7 @@ import { json, string } from "@dbx-tools/shared-core";
63
63
  import isIdentifier from "is-identifier";
64
64
  import { header, isGenerated, makeReadonly, makeWritable, type HeaderOpts } from "./generated.ts";
65
65
  import { moduleExports, moduleStatements, type ModuleExport } from "./module-exports.ts";
66
- import { isModuleFile, toPosix, recordedPackages, repoRoot } from "./packages.ts";
66
+ import { isModuleFile, toPosix, recordedPackages, resolveRepoRoot } from "./packages.ts";
67
67
 
68
68
  /**
69
69
  * A `src`-relative posix path excluded from the root barrel:
@@ -555,8 +555,14 @@ function writeBarrel(file: string, content: string): void {
555
555
  * order, so one unwritable barrel silently left the rest of the repo stale with
556
556
  * nothing in the log to say which packages had been skipped.
557
557
  */
558
- export function generateBarrels(opts: { dirs?: string[] } = {}): number {
559
- const dirs = opts.dirs ?? recordedPackages().map((p) => p.dir);
558
+ export function generateBarrels(
559
+ opts: { dirs?: string[]; includeRoot?: boolean; projectRoot?: string } = {},
560
+ ): number {
561
+ const projectRoot = opts.projectRoot ?? resolveRepoRoot();
562
+ const dirs = opts.dirs ?? [
563
+ ...(opts.includeRoot ? [projectRoot] : []),
564
+ ...recordedPackages(projectRoot).map((p) => p.dir),
565
+ ];
560
566
  let total = 0;
561
567
  const failures: { dir: string; err: unknown }[] = [];
562
568
  for (const dir of dirs) {
@@ -567,7 +573,7 @@ export function generateBarrels(opts: { dirs?: string[] } = {}): number {
567
573
  }
568
574
  }
569
575
  if (failures.length) {
570
- const names = failures.map((f) => relative(repoRoot, f.dir) || f.dir);
576
+ const names = failures.map((f) => relative(projectRoot, f.dir) || f.dir);
571
577
  throw new AggregateError(
572
578
  failures.map((f) => f.err),
573
579
  `${string.pluralize(failures.length, "barrel")} failed: ${names.join(", ")}`,
@@ -3,15 +3,8 @@ import { TextFile, javascript } from "projen";
3
3
 
4
4
  export const BUN_VERSION = "1.3.14";
5
5
 
6
- const cacheKeyScript = `#!/usr/bin/env node
7
- import { createHash } from "node:crypto";
8
- import { readdirSync, readFileSync } from "node:fs";
9
- import { join } from "node:path";
10
-
11
- const root = process.cwd();
12
- const ignored = new Set([
6
+ const DEFAULT_CACHE_IGNORE_PATHS = [
13
7
  ".git",
14
- ".docs-build",
15
8
  ".venv",
16
9
  ".worktrees",
17
10
  "coverage",
@@ -19,12 +12,24 @@ const ignored = new Set([
19
12
  "lib",
20
13
  "node_modules",
21
14
  "target",
22
- ]);
15
+ ] as const;
16
+
17
+ function cacheKeyScript(extraIgnorePaths: readonly string[]): string {
18
+ const ignorePaths = [...new Set(extraIgnorePaths)].sort();
19
+ return `#!/usr/bin/env node
20
+ import { createHash } from "node:crypto";
21
+ import { readdirSync, readFileSync } from "node:fs";
22
+ import { join, sep } from "node:path";
23
+
24
+ const root = process.cwd();
25
+ const ignoredNames = new Set(${JSON.stringify(DEFAULT_CACHE_IGNORE_PATHS)});
26
+ const ignoredPaths = new Set(${JSON.stringify(ignorePaths)});
23
27
  const manifests = [];
24
28
  const walk = (directory) => {
25
29
  for (const entry of readdirSync(directory, { withFileTypes: true })) {
26
- if (ignored.has(entry.name)) continue;
27
30
  const path = join(directory, entry.name);
31
+ const relativePath = path.slice(root.length + 1).split(sep).join("/");
32
+ if (ignoredNames.has(entry.name) || ignoredPaths.has(relativePath)) continue;
28
33
  if (entry.isDirectory()) walk(path);
29
34
  else if (entry.name === "package.json") manifests.push(path);
30
35
  }
@@ -66,15 +71,27 @@ const dependencies = manifests
66
71
  });
67
72
  process.stdout.write(createHash("sha256").update(JSON.stringify(dependencies)).digest("hex"));
68
73
  `;
74
+ }
69
75
 
70
- const configured = new WeakSet<javascript.NodeProject>();
76
+ const configured = new WeakMap<javascript.NodeProject, string>();
71
77
 
72
- function ensureCacheKeyScript(project: javascript.NodeProject): void {
73
- if (configured.has(project)) return;
74
- new TextFile(project.root, ".projen/bun-cache-key.mjs", {
75
- lines: cacheKeyScript.trimEnd().split("\n"),
78
+ function ensureCacheKeyScript(
79
+ project: javascript.NodeProject,
80
+ ignorePaths: readonly string[],
81
+ ): void {
82
+ const root = project.root as javascript.NodeProject;
83
+ const key = JSON.stringify([...new Set(ignorePaths)].sort());
84
+ const existing = configured.get(root);
85
+ if (existing !== undefined) {
86
+ if (existing !== key) {
87
+ throw new Error("Bun workflow cache ignore paths must be consistent across one project");
88
+ }
89
+ return;
90
+ }
91
+ new TextFile(root, ".projen/bun-cache-key.mjs", {
92
+ lines: cacheKeyScript(ignorePaths).trimEnd().split("\n"),
76
93
  });
77
- configured.add(project);
94
+ configured.set(root, key);
78
95
  }
79
96
 
80
97
  function stepCondition(condition: string | undefined, cacheMiss = false): string | undefined {
@@ -88,6 +105,8 @@ function stepCondition(condition: string | undefined, cacheMiss = false): string
88
105
  export interface BunWorkflowCacheOptions {
89
106
  readonly setupCondition?: string;
90
107
  readonly condition?: string;
108
+ /** Repository-relative output paths excluded from dependency manifest hashing. */
109
+ readonly ignorePaths?: readonly string[];
91
110
  }
92
111
 
93
112
  /** Set up Bun and restore its global package cache. */
@@ -95,7 +114,7 @@ export function bunCacheRestoreSteps(
95
114
  project: javascript.NodeProject,
96
115
  options: BunWorkflowCacheOptions = {},
97
116
  ): readonly Record<string, unknown>[] {
98
- ensureCacheKeyScript(project);
117
+ ensureCacheKeyScript(project, options.ignorePaths ?? []);
99
118
  const condition = stepCondition(options.condition);
100
119
  const setupCondition = stepCondition(options.setupCondition);
101
120
  return [
package/src/clean.ts CHANGED
@@ -25,7 +25,7 @@ import { existsSync, rmSync, statSync } from "node:fs";
25
25
  import { basename, join, relative } from "node:path";
26
26
  import { find } from "@dbx-tools/path";
27
27
  import { isReadonly, makeWritable } from "./generated.ts";
28
- import { repoRoot, toPosix } from "./packages.ts";
28
+ import { resolveRepoRoot, toPosix } from "./packages.ts";
29
29
 
30
30
  /**
31
31
  * Basenames `clean` never removes even when they are generated/read-only. `.gitignore`
@@ -40,7 +40,7 @@ const CLEAN_SKIP_FILES: ReadonlySet<string> = new Set([".gitignore"]);
40
40
  * ignores AND every dot-prefixed folder (`.projen`, `.vscode`, `.github`, ...), and
41
41
  * {@link CLEAN_SKIP_FILES} entry (`.gitignore`).
42
42
  */
43
- export function listGeneratedFiles(root: string = repoRoot): string[] {
43
+ export function listGeneratedFiles(root: string = resolveRepoRoot()): string[] {
44
44
  const rel = (f: string): string => toPosix(relative(root, f));
45
45
  return [...find.findFiles("**/*", { cwd: root })]
46
46
  .map((f) => join(root, f))
@@ -57,7 +57,7 @@ export function listGeneratedFiles(root: string = repoRoot): string[] {
57
57
  * own - removing the top-level dir takes it along. Other vendor/build/VCS dirs are
58
58
  * skipped for speed.
59
59
  */
60
- export function listNodeModulesDirs(root: string = repoRoot): string[] {
60
+ export function listNodeModulesDirs(root: string = resolveRepoRoot()): string[] {
61
61
  if (!existsSync(root)) return [];
62
62
  const rel = (f: string): string => toPosix(relative(root, f));
63
63
  return [...find.findFiles("**/node_modules", { cwd: root, ignoreOptions: { dot: false } })]
package/src/codegen.ts CHANGED
@@ -50,7 +50,7 @@ import { log, object } from "@dbx-tools/shared-core";
50
50
  import type * as ts from "typescript";
51
51
  import { lazyRequire } from "./_lazy-require.ts";
52
52
  import { header, isReadonly, makeReadonly, makeWritable } from "./generated.ts";
53
- import { readPackageManifest, repoRoot, recordedPackages } from "./packages.ts";
53
+ import { readPackageManifest, recordedPackages, resolveRepoRoot } from "./packages.ts";
54
54
 
55
55
  const logger = log.logger("projen:codegen");
56
56
 
@@ -115,7 +115,7 @@ export function codegenModulePaths(inputs: readonly string[]): string[] {
115
115
  * package or hoisted to the workspace root. Any other source is treated as
116
116
  * repo-root-relative.
117
117
  */
118
- function resolveInputSource(source: string, fromDir: string): string {
118
+ function resolveInputSource(source: string, fromDir: string, projectRoot: string): string {
119
119
  if (source.startsWith("node_modules/")) {
120
120
  let dir = fromDir;
121
121
  for (;;) {
@@ -126,7 +126,7 @@ function resolveInputSource(source: string, fromDir: string): string {
126
126
  dir = parent;
127
127
  }
128
128
  }
129
- return resolve(repoRoot, source);
129
+ return resolve(projectRoot, source);
130
130
  }
131
131
 
132
132
  /**
@@ -276,6 +276,7 @@ function isGeneratedModule(srcDir: string, file: string): boolean {
276
276
  function generatePackage(
277
277
  tsRuntime: typeof ts,
278
278
  generate: typeof import("ts-to-zod").generate,
279
+ projectRoot: string,
279
280
  dir: string,
280
281
  inputs: string[],
281
282
  ): string {
@@ -296,7 +297,7 @@ function generatePackage(
296
297
 
297
298
  let warnings = 0;
298
299
  for (const input of parsed) {
299
- const sourcePath = resolveInputSource(input.source, dir);
300
+ const sourcePath = resolveInputSource(input.source, dir, projectRoot);
300
301
  if (!existsSync(sourcePath)) {
301
302
  throw new Error(`codegen input not found: ${input.source}`);
302
303
  }
@@ -336,9 +337,16 @@ function generatePackage(
336
337
  * `codegen` field. Returns the package dirs it wrote so the caller can rebuild
337
338
  * their barrels. `ts-to-zod` + `typescript` are lazy-loaded.
338
339
  */
339
- export function generateCodegen(): string[] {
340
- const targets = recordedPackages()
341
- .map((p) => ({ dir: p.dir, inputs: codegenInputs(p.dir) }))
340
+ export function generateCodegen(
341
+ projectRoot: string = resolveRepoRoot(),
342
+ options: { includeRoot?: boolean } = {},
343
+ ): string[] {
344
+ const packageDirs = [
345
+ ...(options.includeRoot ? [projectRoot] : []),
346
+ ...recordedPackages(projectRoot).map((pkg) => pkg.dir),
347
+ ];
348
+ const targets = packageDirs
349
+ .map((dir) => ({ dir, inputs: codegenInputs(dir) }))
342
350
  .filter((t): t is { dir: string; inputs: string[] } => t.inputs !== undefined);
343
351
 
344
352
  // Nothing to generate is the NORMAL case for a workspace that declares no
@@ -356,7 +364,7 @@ export function generateCodegen(): string[] {
356
364
 
357
365
  const written: string[] = [];
358
366
  for (const target of targets) {
359
- written.push(generatePackage(tsRuntime, generate, target.dir, target.inputs));
367
+ written.push(generatePackage(tsRuntime, generate, projectRoot, target.dir, target.inputs));
360
368
  }
361
369
  return written;
362
370
  }
package/src/packages.ts CHANGED
@@ -30,12 +30,13 @@ import { find } from "@dbx-tools/path";
30
30
  import { json, object, string } from "@dbx-tools/shared-core";
31
31
  import { parse } from "yaml";
32
32
 
33
- /**
34
- * The repo root: the nearest package/projenrc root, falling back to the current
35
- * working directory. Detection (npm prefix, git top-level, root markers) lives
36
- * in `@dbx-tools/core`'s {@link coreProject.root}.
37
- */
38
- export const repoRoot = coreProject.root() ?? process.cwd();
33
+ /** Resolve the nearest package/projenrc root at call time. */
34
+ export function resolveRepoRoot(cwd: string = process.cwd()): string {
35
+ return coreProject.root(cwd) ?? cwd;
36
+ }
37
+
38
+ /** Current-process repository root for executable task entrypoints. */
39
+ export const repoRoot = resolveRepoRoot();
39
40
 
40
41
  /**
41
42
  * Default package roots. Each is scanned for packages; override via the
@@ -49,8 +50,8 @@ export const DEFAULT_PACKAGE_ROOTS = ["packages"] as const;
49
50
  * {@link coreProject.name}, which is also what a consuming repo's own tooling
50
51
  * sees, so the engine and its host agree on the name.
51
52
  */
52
- export function projectName(): string {
53
- return coreProject.name(repoRoot);
53
+ export function projectName(projectRoot: string = resolveRepoRoot()): string {
54
+ return coreProject.name(projectRoot);
54
55
  }
55
56
 
56
57
  const MODULE_EXTS = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
@@ -168,7 +169,7 @@ export class DiscoveredPackage {
168
169
  }
169
170
 
170
171
  /** Read the raw workspace member globs from `pnpm-workspace.yaml` (source of truth). */
171
- function readRecordedMembers(projectRoot: string = repoRoot): string[] {
172
+ function readRecordedMembers(projectRoot: string = resolveRepoRoot()): string[] {
172
173
  const file = resolve(projectRoot, "pnpm-workspace.yaml");
173
174
  if (!existsSync(file)) return [];
174
175
  const doc = parse(readFileSync(file, "utf8")) as {
@@ -218,7 +219,7 @@ function collectPackageDirs(rootAbs: string): string[] {
218
219
  * recorded set. Sorted by member path.
219
220
  */
220
221
  export function scanPackages(
221
- projectRoot: string = repoRoot,
222
+ projectRoot: string = resolveRepoRoot(),
222
223
  roots: readonly string[] = DEFAULT_PACKAGE_ROOTS,
223
224
  ): DiscoveredPackage[] {
224
225
  const out: DiscoveredPackage[] = [];
@@ -285,7 +286,7 @@ function readManifestTags(dir: string): string[] | undefined {
285
286
  * the root `package.json` `dbxToolsConfig.syncResynthPaths` (set via the
286
287
  * {@link DBXToolsProjectOptions.syncResynthPaths} option at synth).
287
288
  */
288
- export function syncResynthPaths(projectRoot: string = repoRoot): string[] {
289
+ export function syncResynthPaths(projectRoot: string = resolveRepoRoot()): string[] {
289
290
  const paths = readDbxToolsConfig(projectRoot)?.syncResynthPaths;
290
291
  return Array.isArray(paths) ? string.parseList(paths.map((p) => String(p))) : [];
291
292
  }
@@ -297,7 +298,7 @@ export function syncResynthPaths(projectRoot: string = repoRoot): string[] {
297
298
  * authoritative, so it reflects the resolved tag set.
298
299
  * Sorted by path.
299
300
  */
300
- export function recordedPackages(projectRoot: string = repoRoot): RecordedPackage[] {
301
+ export function recordedPackages(projectRoot: string = resolveRepoRoot()): RecordedPackage[] {
301
302
  const out: RecordedPackage[] = [];
302
303
  for (const member of readRecordedMembers(projectRoot)) {
303
304
  const pkg = packageOfMember(projectRoot, member);
@@ -318,7 +319,7 @@ export function recordedPackages(projectRoot: string = repoRoot): RecordedPackag
318
319
  * that field fall back to the distinct first segment of every recorded member,
319
320
  * unioned with the defaults.
320
321
  */
321
- export function recordedRoots(projectRoot: string = repoRoot): string[] {
322
+ export function recordedRoots(projectRoot: string = resolveRepoRoot()): string[] {
322
323
  const configured = readDbxToolsConfig(projectRoot)?.packageRoots;
323
324
  if (Array.isArray(configured)) {
324
325
  const roots = string.parseList(configured.map((root) => String(root)));