@dbx-tools/projen 0.6.112 → 0.6.114

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
@@ -53,6 +53,9 @@ new package membership.
53
53
  without turning Python packages into JavaScript workspace projects. It generates
54
54
  the root and member `pyproject.toml` files, standard `py:*` tasks, the VS Code
55
55
  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.
56
59
 
57
60
  ```ts
58
61
  import { project, projectPy } from "@dbx-tools/projen";
package/package.json CHANGED
@@ -26,9 +26,9 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "@clack/prompts": "^1.7.0",
29
- "@dbx-tools/core": "0.6.112",
30
- "@dbx-tools/path": "0.6.112",
31
- "@dbx-tools/shared-core": "0.6.112",
29
+ "@dbx-tools/core": "0.6.114",
30
+ "@dbx-tools/path": "0.6.114",
31
+ "@dbx-tools/shared-core": "0.6.114",
32
32
  "commander": "^15.0.0",
33
33
  "concurrently": "^10.0.3",
34
34
  "constructs": "^10.6.0",
@@ -36,6 +36,7 @@
36
36
  "openapi-typescript": "^7.13.0",
37
37
  "oxc-parser": "^0.90.0",
38
38
  "projen": "^0.101.16",
39
+ "smol-toml": "^1.7.1",
39
40
  "ts-to-zod": "^5.1.0",
40
41
  "yaml": "^2.9.0"
41
42
  },
@@ -47,7 +48,7 @@
47
48
  },
48
49
  "main": "index.ts",
49
50
  "license": "Apache-2.0",
50
- "version": "0.6.112",
51
+ "version": "0.6.114",
51
52
  "types": "index.ts",
52
53
  "type": "module",
53
54
  "exports": {
package/src/packages.ts CHANGED
@@ -314,12 +314,16 @@ export function recordedPackages(projectRoot: string = repoRoot): RecordedPackag
314
314
  }
315
315
 
316
316
  /**
317
- * The roots to scan for a live filesystem check: the distinct first segment of
318
- * every recorded member, unioned with the defaults. Lets a command compare disk
319
- * against the recorded truth without knowing the `packageRoots` the last
320
- * synth was configured with.
317
+ * The configured roots recorded in the root manifest. Older workspaces without
318
+ * that field fall back to the distinct first segment of every recorded member,
319
+ * unioned with the defaults.
321
320
  */
322
321
  export function recordedRoots(projectRoot: string = repoRoot): string[] {
322
+ const configured = readDbxToolsConfig(projectRoot)?.packageRoots;
323
+ if (Array.isArray(configured)) {
324
+ const roots = string.parseList(configured.map((root) => String(root)));
325
+ if (roots.length) return roots;
326
+ }
323
327
  const roots = new Set<string>(DEFAULT_PACKAGE_ROOTS);
324
328
  for (const member of readRecordedMembers(projectRoot)) {
325
329
  const pkg = packageOfMember(projectRoot, member);
package/src/project-js.ts CHANGED
@@ -611,9 +611,10 @@ export class DBXToolsNodeProject
611
611
  *
612
612
  * Every projen child has its own `NodePackage` post-synth hook, which otherwise
613
613
  * runs `bun install` against the same root workspace once per package. Clear the
614
- * child install tasks while leaving the root's real install/install:ci tasks
615
- * intact. Applied in root `preSynthesize` so manually attached late children are
616
- * included and repeated synths remain idempotent.
614
+ * child install tasks and suppress the package hook's install call and trigger
615
+ * log while leaving dependency resolution and the root's real install/install:ci
616
+ * tasks intact. Applied in root `preSynthesize` so manually attached late
617
+ * children are included and repeated synths remain idempotent.
617
618
  */
618
619
  export const ROOT_INSTALL_ONLY_MIXIN = mixin.create(
619
620
  (construct: IConstruct): construct is DBXToolsNodeProject | DBXToolsTypeScriptProject =>
@@ -622,6 +623,12 @@ export const ROOT_INSTALL_ONLY_MIXIN = mixin.create(
622
623
  (child) => {
623
624
  child.package.installTask.reset();
624
625
  child.package.installCiTask.reset();
626
+ const nodePackage = child.package as unknown as {
627
+ installDependencies(trigger: unknown): void;
628
+ logInstallTrigger(trigger: unknown): void;
629
+ };
630
+ nodePackage.installDependencies = () => {};
631
+ nodePackage.logInstallTrigger = () => {};
625
632
  },
626
633
  );
627
634
 
@@ -1014,6 +1021,8 @@ function initProject(
1014
1021
  // Root carries the bare `repository` (no `directory`); children add their subpath.
1015
1022
  applyRepository(project, options.repository);
1016
1023
 
1024
+ const roots = options.packageRoots ?? DEFAULT_PACKAGE_ROOTS;
1025
+ project.dbxToolsConfig.packageRoots = [...roots];
1017
1026
  if (options.syncResynthPaths?.length) {
1018
1027
  project.dbxToolsConfig.syncResynthPaths = [...options.syncResynthPaths];
1019
1028
  }
@@ -1040,7 +1049,6 @@ function initProject(
1040
1049
  // local editor state. Both ignore CONTENTS (`.idea/*`) rather than the
1041
1050
  // directory, so a later `!` negation can still reach a file inside.
1042
1051
  project.gitignore.addPatterns(".env", ".env.*", "!.env.example", "!.env.sample", ".idea/*");
1043
- const roots = options.packageRoots ?? DEFAULT_PACKAGE_ROOTS;
1044
1052
  for (const root of roots) {
1045
1053
  project.annotateGenerated(`/${root}/**/index.ts`);
1046
1054
  project.annotateGenerated(`/${root}/openapi/**`);
package/src/project-py.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  /** Reusable uv workspace generation for Python packages hosted in a projen tree. */
2
2
  import { string } from "@dbx-tools/shared-core";
3
3
  import { Component, type Project, javascript, python, vscode } from "projen";
4
+ import type { IResolver } from "projen/lib/file";
4
5
  import { GithubWorkflow } from "projen/lib/github";
5
6
  import { JobPermission } from "projen/lib/github/workflows-model";
7
+ import { parse, stringify } from "smol-toml";
6
8
  import type { DBXToolsProject, DBXToolsProjectOptions } from "./project.ts";
7
9
  import { readWorkspaceVersion } from "./workspace-version.ts";
8
10
 
@@ -45,7 +47,11 @@ export interface PythonReleaseOptions {
45
47
  export interface DBXToolsPythonWorkspaceOptions {
46
48
  readonly packages: readonly PythonPackageOptions[];
47
49
  readonly repository: PythonRepositoryOptions;
50
+ /** Workspace packages exposed as commands from the repository root. */
51
+ readonly dependencies?: readonly string[];
48
52
  readonly requiresPython?: string;
53
+ /** uv strategy for repositories that intentionally use multiple trusted indexes. */
54
+ readonly indexStrategy?: "first-index" | "unsafe-first-match" | "unsafe-best-match";
49
55
  readonly ruffTarget?: string;
50
56
  readonly workspaceName?: string;
51
57
  readonly devDependencies?: readonly string[];
@@ -65,6 +71,24 @@ const DEFAULT_DEV_DEPENDENCIES = [
65
71
 
66
72
  const quote = (value: string): string => JSON.stringify(value);
67
73
 
74
+ interface TomlSynthesizer {
75
+ synthesizeContent(resolver: IResolver): string | undefined;
76
+ }
77
+
78
+ function formatPyproject(file: python.PyprojectTomlFile): void {
79
+ const target = file as unknown as TomlSynthesizer;
80
+ const synthesize = target.synthesizeContent.bind(file);
81
+ target.synthesizeContent = (resolver) => {
82
+ const content = synthesize(resolver);
83
+ if (!content) return content;
84
+ const marker = content.startsWith("# ") ? content.slice(0, content.indexOf("\n")) : undefined;
85
+ const body = stringify(parse(content))
86
+ .trimEnd()
87
+ .replace(/ = \[ (.*) \]$/gm, " = [$1]");
88
+ return `${marker ? `${marker}\n\n` : ""}${body}\n`;
89
+ };
90
+ }
91
+
68
92
  /** Repository-relative path for a Python package directory. */
69
93
  export function pythonPackagePath(repository: PythonRepositoryOptions, directory: string): string {
70
94
  return `${repository.root ?? "packages/py"}/${directory}`;
@@ -147,6 +171,7 @@ export class DBXToolsPythonProject extends python.PythonProject implements DBXTo
147
171
  if (pkg.scripts) {
148
172
  this.uv.file.addOverride("project.scripts", pkg.scripts);
149
173
  }
174
+ formatPyproject(this.uv.file);
150
175
  this.uv.file.readonly = true;
151
176
 
152
177
  for (const path of [".gitattributes", ".gitignore"]) {
@@ -230,7 +255,7 @@ export class DBXToolsPythonWorkspace extends Component {
230
255
  name: options.workspaceName ?? `${string.toSlug(project.name)}-python-workspace`,
231
256
  version: this.version,
232
257
  requiresPython: this.requiresPython,
233
- dependencies: [],
258
+ dependencies: [...(options.dependencies ?? [])],
234
259
  },
235
260
  dependencyGroups: {
236
261
  dev: [...(options.devDependencies ?? DEFAULT_DEV_DEPENDENCIES)],
@@ -255,10 +280,14 @@ export class DBXToolsPythonWorkspace extends Component {
255
280
  },
256
281
  },
257
282
  });
283
+ if (options.indexStrategy) {
284
+ file.addOverride("tool.uv.index-strategy", options.indexStrategy);
285
+ }
258
286
  file.addOverride(
259
287
  "tool.uv.sources",
260
288
  Object.fromEntries(options.packages.map((pkg) => [pkg.name, { workspace: true }])),
261
289
  );
290
+ formatPyproject(file);
262
291
  file.readonly = true;
263
292
  return file;
264
293
  }