@dbx-tools/projen 0.9.40 → 0.9.46

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 (52) hide show
  1. package/README.md +58 -67
  2. package/index.ts +6 -8
  3. package/package.json +9 -10
  4. package/shims/python-node/bootstrap.ts +129 -0
  5. package/shims/python-node/child-process.ts +96 -0
  6. package/shims/python-node/crypto.ts +60 -0
  7. package/shims/python-node/fs-promises.ts +179 -0
  8. package/shims/python-node/fs.ts +173 -0
  9. package/shims/python-node/headers.ts +75 -0
  10. package/shims/python-node/host.ts +341 -0
  11. package/shims/python-node/os.ts +20 -0
  12. package/shims/python-node/process.ts +13 -0
  13. package/shims/python-node/readline.ts +37 -0
  14. package/shims/python-node/tsconfig.json +24 -0
  15. package/src/_lazy-require.ts +7 -25
  16. package/src/bun-app.ts +2 -2
  17. package/src/bun-workflow.ts +2 -170
  18. package/src/codegen.ts +23 -20
  19. package/src/dbx-tools-config.ts +52 -9
  20. package/src/generated.ts +4 -17
  21. package/src/mixin.ts +1 -5
  22. package/src/packages.ts +18 -15
  23. package/src/pnpm-workspace.ts +1 -4
  24. package/src/project-js.ts +65 -196
  25. package/src/project-predicate.ts +7 -3
  26. package/src/project-py.ts +127 -37
  27. package/src/publish.ts +13 -10
  28. package/src/python-node-bindings.ts +2 -0
  29. package/src/python-node-bundle.ts +6 -0
  30. package/src/release-options.ts +17 -17
  31. package/src/release.ts +31 -14
  32. package/src/scaffold.ts +4 -7
  33. package/src/tags.ts +6 -16
  34. package/src/vscode.ts +1 -1
  35. package/src/watch.ts +5 -5
  36. package/src/workspace-lock.ts +1 -0
  37. package/src/workspace-version.ts +4 -4
  38. package/tasks/barrels.ts +3 -5
  39. package/tasks/clean.ts +1 -0
  40. package/tasks/lib/compile-workspace.ts +41 -19
  41. package/tasks/lib/local-publish.ts +0 -28
  42. package/tasks/lib/publish-npm.ts +8 -0
  43. package/tasks/lib/publish-python.ts +4 -0
  44. package/tasks/lib/python-node-bindings.ts +107 -26
  45. package/tasks/lib/python-registry.ts +4 -0
  46. package/tasks/lib/python-release.ts +2 -0
  47. package/tasks/lib/release.ts +2 -2
  48. package/tasks/projenrc.ts +1 -0
  49. package/tasks/sync.ts +4 -4
  50. package/src/openapi.ts +0 -293
  51. package/tasks/local-publish.ts +0 -6
  52. package/tasks/openapi.ts +0 -35
package/src/project-js.ts CHANGED
@@ -12,13 +12,13 @@ import * as projectUtils from "@dbx-tools/core/project-utils";
12
12
  import { ignore, match } from "@dbx-tools/path";
13
13
  import { object, stringUtils, type OneOrMany } from "@dbx-tools/shared-core";
14
14
  import { type IConstruct } from "constructs";
15
- import { Component, IgnoreFile, Project, javascript, typescript } from "projen";
15
+ import { Component, LogLevel, Project, javascript, typescript } from "projen";
16
16
  import { JobPermission, type JobStep } from "projen/lib/github/workflows-model";
17
17
  import type { ReleaseProjectOptions } from "projen/lib/release";
18
18
  import { generateBarrels } from "./barrels.ts";
19
19
 
20
20
  import { BUN_APP_OVERRIDES, RootBunfigFile } from "./bun-app.ts";
21
- import { BUN_VERSION, bunCacheRestoreSteps, bunCacheSaveStep } from "./bun-workflow.ts";
21
+ import { BUN_VERSION } from "./bun-workflow.ts";
22
22
  import { codegenModulePaths, generateCodegen } from "./codegen.ts";
23
23
  import { DBXToolsConfig, type DBXToolsConfigOptions } from "./dbx-tools-config.ts";
24
24
  import { resolvePkgRoot } from "./engine-root.ts";
@@ -52,6 +52,8 @@ export interface DBXToolsJavaScriptProject extends DBXToolsProject, javascript.N
52
52
  readonly dbxToolsConfig: DBXToolsConfig;
53
53
  /** npm scope (the `@scope` in `@scope/pkg`), without the leading `@`. */
54
54
  readonly scope: string;
55
+ /** Branch that the repository's tag release must match. */
56
+ readonly releaseBranch: string;
55
57
 
56
58
  /**
57
59
  * The `pnpm-workspace.yaml` catalog / member / build-allowance state - only a
@@ -61,8 +63,6 @@ export interface DBXToolsJavaScriptProject extends DBXToolsProject, javascript.N
61
63
  pnpmWorkspace?: PnpmWorkspaceState;
62
64
  /** Root projenrc tsconfigs - only a tree ROOT has one. */
63
65
  rootTsconfig?: DBXToolsRootTsconfig;
64
- /** Repository outputs excluded from generated workflow dependency-cache hashing. */
65
- readonly workflowCacheIgnorePaths: readonly string[];
66
66
  }
67
67
 
68
68
  /** Parsed npm package identifier: optional scope plus the unscoped package name. */
@@ -253,11 +253,8 @@ export function projectRepositoryUrl(project: javascript.NodeProject): string |
253
253
  }
254
254
 
255
255
  /** Resolve the branch release workflows use for a generated project. */
256
- export function projectReleaseBranch(project: javascript.NodeProject): string {
257
- return (
258
- (project as javascript.NodeProject & { readonly releaseBranch?: string }).releaseBranch ??
259
- "main"
260
- );
256
+ export function projectReleaseBranch(project: DBXToolsJavaScriptProject): string {
257
+ return project.releaseBranch;
261
258
  }
262
259
 
263
260
  /**
@@ -369,19 +366,21 @@ const PRETTIER_SETTINGS: javascript.PrettierSettings = {
369
366
  export { PROJEN_VERSION } from "./projen-version.ts";
370
367
 
371
368
  /** SPDX license shared by generated JavaScript and Python packages. */
372
- export const DBX_TOOLS_LICENSE = "Apache-2.0";
369
+ export const LICENSE = "Apache-2.0";
373
370
 
374
371
  /**
375
372
  * The engine's opinionated `NodeProject` defaults. A caller's own options override
376
373
  * these (they are spread AFTER this). Root-only concerns key off `options.parent`,
377
374
  * NOT the class: only the tree ROOT (no parent) turns on projen's built-in Prettier
378
375
  * (the `prettier` devDep + `.prettierrc.json` + `.prettierignore`), so a child package
379
- * inherits the root's config rather than emitting its own. `name`/`defaultReleaseBranch`
380
- * are resolved/applied by the caller.
376
+ * inherits the root's config rather than emitting its own. Projen's informational
377
+ * status messages are hidden by default while task output, warnings, and errors
378
+ * remain visible. `name`/`defaultReleaseBranch` are resolved/applied by the caller.
381
379
  */
382
380
  function defaultProjectOptions(options: DBXToolsJavaScriptProjectOptions) {
383
381
  const isRoot = options.parent === undefined;
384
382
  return {
383
+ logging: { level: LogLevel.WARN },
385
384
  // Bun owns install/run/build/test locally and in CI. projen renders
386
385
  // `bun install`/`bunx` and a native `trustedDependencies` field from this.
387
386
  // The engine still emits `pnpm-workspace.yaml` itself (see
@@ -410,7 +409,7 @@ function defaultProjectOptions(options: DBXToolsJavaScriptProjectOptions) {
410
409
  // workflow opts in per run through `npm_config_provenance`, so local publishes
411
410
  // to Verdaccio still work without a CI OIDC provider. See
412
411
  // {@link DBXToolsRelease}.
413
- ...(isRoot ? {} : { npmAccess: javascript.NpmAccess.PUBLIC }),
412
+ ...(isRoot || options.publishable === false ? {} : { npmAccess: javascript.NpmAccess.PUBLIC }),
414
413
  workflowPackageCache: false,
415
414
  // The root build validates the whole workspace and must not also pack every
416
415
  // member into unused `dist/js` tarballs. Child projects keep projen's package
@@ -457,9 +456,8 @@ function defaultProjectOptions(options: DBXToolsJavaScriptProjectOptions) {
457
456
  /**
458
457
  * `gitIgnoreOptions` with its `ignorePatterns` array CLONED, for handing to a
459
458
  * projen `Project` constructor: projen's IgnoreFile ALIASES the array it is given
460
- * (every later addPatterns call mutates it), so the throwaway default-laden
461
- * `.gitignore` gets a copy - {@link swapChildGitignore} re-reads the caller's
462
- * pristine array to seed a child's fresh one. Spread AFTER `...options`.
459
+ * and every later `addPatterns` call mutates it. The caller's typed options must
460
+ * remain unchanged while Projen owns and extends the generated ignore file.
463
461
  */
464
462
  function copiedGitIgnoreOptions(
465
463
  options: DBXToolsJavaScriptProjectOptions,
@@ -516,11 +514,20 @@ function validateReleaseOptions(options: DBXToolsJavaScriptProjectOptions): void
516
514
  /** Options for {@link DBXToolsNodeProject} (the monorepo root). */
517
515
  export type DBXToolsReleaseMode = "dbx-tools" | "disabled";
518
516
 
517
+ /** Native constructor fields resolved for one filesystem-discovered package. */
518
+ export interface DiscoveredTypeScriptPackageOptions {
519
+ readonly name: string;
520
+ readonly description?: string;
521
+ readonly entrypoint?: string;
522
+ readonly publishable?: boolean;
523
+ }
524
+
519
525
  type SupportedWorkflowOptions = Pick<
520
526
  ReleaseProjectOptions,
521
527
  "postBuildSteps" | "workflowContainerImage" | "workflowRunsOn" | "workflowRunsOnGroup"
522
528
  >;
523
529
 
530
+ /** Options shared by discovered package roots and standalone JavaScript projects. */
524
531
  export type DBXToolsJavaScriptProjectOptions = CommonProjectOptions &
525
532
  Partial<
526
533
  Omit<javascript.NodeProjectOptions, keyof ReleaseProjectOptions | "release" | "releaseToNpm">
@@ -548,8 +555,11 @@ export type DBXToolsJavaScriptProjectOptions = CommonProjectOptions &
548
555
  * disable. Defaults to `"node"`.
549
556
  */
550
557
  readonly omitRelativePrefix?: OneOrMany<string>;
551
- /** Resolve a discovered package name before its project is constructed. */
552
- readonly resolvePackageName?: (pkg: DiscoveredPackage, defaultPackageName: string) => string;
558
+ /** Resolve native constructor fields before a discovered package is constructed. */
559
+ readonly resolvePackageOptions?: (
560
+ pkg: DiscoveredPackage,
561
+ defaults: DiscoveredTypeScriptPackageOptions,
562
+ ) => DiscoveredTypeScriptPackageOptions;
553
563
  /**
554
564
  * Maps a path token / relPath / glob to tag(s), unioned into a package's
555
565
  * path-derived tags. Defaults to an identity map over the known tag names; a
@@ -575,14 +585,14 @@ export type DBXToolsJavaScriptProjectOptions = CommonProjectOptions &
575
585
  readonly releaseValidationTasks?: readonly string[];
576
586
  /** Release build prerequisites installed before repository validation tasks. */
577
587
  readonly releaseSetupSteps?: readonly JobStep[];
588
+ /** Repository-specific synthesis commands run before release source verification. */
589
+ readonly releaseSynthesisCommands?: readonly string[];
578
590
  /** Set to `false` to omit normal npm workspace publication. */
579
591
  readonly nodeRelease?: boolean;
580
592
  /** Unified dbx-tools release workflow, or no release surface. Defaults to `dbx-tools`. */
581
593
  readonly releaseMode?: DBXToolsReleaseMode;
582
594
  /** Prefix for generated release tags. Defaults to `v`. */
583
595
  readonly releaseTagPrefix?: string;
584
- /** Repository output paths excluded from generated workflow dependency-cache hashing. */
585
- readonly workflowCacheIgnorePaths?: readonly string[];
586
596
  /**
587
597
  * Extra workspace member paths (repo-relative, POSIX) to list in the workspace
588
598
  * config ALONGSIDE the discovered `packageRoots` members - for a package that
@@ -609,16 +619,6 @@ export type DBXToolsTypeScriptProjectOptions = Partial<
609
619
  > &
610
620
  DBXToolsJavaScriptProjectOptions;
611
621
 
612
- const initializingWorkflowCachePaths = new Map<string, readonly string[]>();
613
-
614
- function configuredOutdir(
615
- options: Pick<DBXToolsJavaScriptProjectOptions, "parent" | "outdir">,
616
- ): string {
617
- return options.parent
618
- ? resolve(options.parent.outdir, options.outdir ?? ".")
619
- : resolve(options.outdir ?? process.cwd());
620
- }
621
-
622
622
  /**
623
623
  * A monorepo root. Scans `packageRoots` and appends a
624
624
  * {@link DBXToolsTypeScriptProject} per `src`-bearing folder, then emits the
@@ -635,7 +635,6 @@ export class DBXToolsNodeProject
635
635
  rootTsconfig?: DBXToolsRootTsconfig;
636
636
  readonly extraWorkspaceMembers: readonly string[];
637
637
  readonly releaseBranch: string;
638
- readonly workflowCacheIgnorePaths: readonly string[];
639
638
  private readonly rootInstallOnly: boolean;
640
639
 
641
640
  constructor(options: DBXToolsJavaScriptProjectOptions = {}) {
@@ -648,8 +647,6 @@ export class DBXToolsNodeProject
648
647
  // into `package.json` (`workspaces`/`catalog`) for bun to read. The
649
648
  // `pnpm-workspace.yaml` is still emitted for the Databricks Apps pnpm install.
650
649
  const pnpmWorkspace = new PnpmWorkspaceState(options);
651
- const outdir = configuredOutdir(options);
652
- initializingWorkflowCachePaths.set(outdir, options.workflowCacheIgnorePaths ?? []);
653
650
  super({
654
651
  ...defaultProjectOptions(options),
655
652
  pnpmOptions: {
@@ -668,40 +665,11 @@ export class DBXToolsNodeProject
668
665
  this.scope = scope;
669
666
  this.extraWorkspaceMembers = options.extraWorkspaceMembers ?? [];
670
667
  this.releaseBranch = options.defaultReleaseBranch ?? "main";
671
- this.workflowCacheIgnorePaths = options.workflowCacheIgnorePaths ?? [];
672
- initializingWorkflowCachePaths.delete(outdir);
673
668
  this.rootInstallOnly = options.rootInstallOnly !== false;
674
669
  this.dbxToolsConfig = new DBXToolsConfig(this, options);
675
670
  initProject(this, options);
676
671
  }
677
672
 
678
- public override renderWorkflowSetup(options?: javascript.RenderWorkflowSetupOptions): JobStep[] {
679
- const steps = super.renderWorkflowSetup(options);
680
- if (this.parent) return steps;
681
- return steps.flatMap((step) => {
682
- if (step.uses?.startsWith("oven-sh/setup-bun@")) {
683
- return [
684
- ...bunCacheRestoreSteps(this, {
685
- ignorePaths:
686
- this.workflowCacheIgnorePaths ??
687
- initializingWorkflowCachePaths.get(resolve(this.outdir)) ??
688
- [],
689
- }),
690
- ];
691
- }
692
- if (step.run?.startsWith("bun install")) {
693
- return [
694
- step,
695
- bunCacheSaveStep({
696
- condition:
697
- "github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository",
698
- }),
699
- ];
700
- }
701
- return [step];
702
- }) as JobStep[];
703
- }
704
-
705
673
  public override preSynthesize(): void {
706
674
  prepareRootSynthesis(this, this.rootInstallOnly);
707
675
  super.preSynthesize();
@@ -714,11 +682,11 @@ export class DBXToolsNodeProject
714
682
  * Root-owned workspace install policy.
715
683
  *
716
684
  * Every projen child has its own `NodePackage` post-synth hook, which otherwise
717
- * runs `bun install` against the same root workspace once per package. Clear the
718
- * child install tasks and suppress the package hook's install call and trigger
719
- * log while leaving dependency resolution and the root's real install/install:ci
720
- * tasks intact. Applied in root `preSynthesize` so manually attached late
721
- * children are included and repeated synths remain idempotent.
685
+ * runs the install task against the same root workspace once per package. Clear
686
+ * the public child install tasks while leaving dependency resolution and the
687
+ * root's real install/install:ci tasks intact. Applied in root `preSynthesize`
688
+ * so manually attached late children are included and repeated synths remain
689
+ * idempotent.
722
690
  */
723
691
  export const ROOT_INSTALL_ONLY_MIXIN = mixin.create(
724
692
  (construct: IConstruct): construct is DBXToolsNodeProject | DBXToolsTypeScriptProject =>
@@ -727,12 +695,6 @@ export const ROOT_INSTALL_ONLY_MIXIN = mixin.create(
727
695
  (child) => {
728
696
  child.package.installTask.reset();
729
697
  child.package.installCiTask.reset();
730
- const nodePackage = child.package as unknown as {
731
- installDependencies(trigger: unknown): void;
732
- logInstallTrigger(trigger: unknown): void;
733
- };
734
- nodePackage.installDependencies = () => {};
735
- nodePackage.logInstallTrigger = () => {};
736
698
  },
737
699
  );
738
700
 
@@ -754,7 +716,6 @@ export class DBXToolsTypeScriptProject
754
716
  rootTsconfig?: DBXToolsRootTsconfig;
755
717
  readonly extraWorkspaceMembers: readonly string[];
756
718
  readonly releaseBranch: string;
757
- readonly workflowCacheIgnorePaths: readonly string[];
758
719
  private readonly rootInstallOnly: boolean;
759
720
 
760
721
  constructor(options: DBXToolsTypeScriptProjectOptions) {
@@ -795,7 +756,6 @@ export class DBXToolsTypeScriptProject
795
756
  this.scope = scope;
796
757
  this.extraWorkspaceMembers = options.extraWorkspaceMembers ?? [];
797
758
  this.releaseBranch = options.defaultReleaseBranch ?? "main";
798
- this.workflowCacheIgnorePaths = options.workflowCacheIgnorePaths ?? [];
799
759
  this.rootInstallOnly = options.rootInstallOnly !== false;
800
760
  // Pairs with `jsx` in SHARED_COMPILER_OPTIONS: projen's default `include` is
801
761
  // `src/**/*.ts` only, which silently omits a `.tsx` file from the program
@@ -862,10 +822,9 @@ class GeneratedSource extends Component {
862
822
  public override preSynthesize(): void {
863
823
  const root = resolve(this.project.outdir);
864
824
  for (const subproject of this.project.subprojects) {
865
- if (!(subproject instanceof javascript.NodeProject)) continue;
866
- const codegen = subproject.package.manifest.codegen as { inputs?: string[] } | undefined;
825
+ if (!(subproject instanceof DBXToolsTypeScriptProject)) continue;
867
826
  const packagePath = toPosix(relative(root, subproject.outdir));
868
- const modules = codegenModulePaths(codegen?.inputs ?? []);
827
+ const modules = codegenModulePaths(subproject.dbxToolsConfig.codegenInputs);
869
828
  for (const module of modules) {
870
829
  this.project.annotateGenerated(`/${packagePath}/${module}`);
871
830
  }
@@ -898,10 +857,10 @@ class GeneratedSource extends Component {
898
857
  *
899
858
  * projen gives a monorepo root empty `compile`/`test` tasks - a child's tasks
900
859
  * are the child's business - so `bun run build` at the root type-checked nothing
901
- * and ran no package tests. Compilation groups ordinary `tsc --build` members
902
- * into a few TypeScript processes and runs custom compile tasks alongside them. Tests
903
- * still use Bun's filtered workspace fan-out. Both read the current workspace
904
- * list from `package.json`, so a newly added member needs no re-synth.
860
+ * and ran no package tests. Compilation groups ordinary package tsconfigs into
861
+ * a few TypeScript processes and runs custom package lifecycles alongside them.
862
+ * Tests still use Bun's filtered workspace fan-out. Both read the current
863
+ * workspace list from `package.json`, so a newly added member needs no re-synth.
905
864
  *
906
865
  * `*` matches every workspace MEMBER and never the root itself, so the test
907
866
  * task delegating to it cannot recurse. Members declared outside the scanned
@@ -923,35 +882,9 @@ class WorkspaceValidationTasks extends Component {
923
882
  }
924
883
 
925
884
  /**
926
- * Apply bounded, read-only, supersedable defaults to validation workflows.
927
- *
928
- * Projen otherwise leaves jobs at GitHub's six-hour ceiling. Missing workflows
929
- * are a no-op, so roots that keep the engine defaults (`github`/build workflow
930
- * off) do not gain new files.
931
- */
932
- class WorkflowDefaults extends Component {
933
- public override preSynthesize(): void {
934
- const build = this.project.tryFindObjectFile(".github/workflows/build.yml");
935
- build?.addOverride("on.pull_request.types", ["opened", "synchronize", "reopened", "closed"]);
936
- build?.addOverride(
937
- "jobs.build.if",
938
- "${{ github.event_name != 'pull_request' || github.event.action != 'closed' }}",
939
- );
940
- for (const job of ["build", "self-mutation"]) {
941
- build?.addOverride(`jobs.${job}.timeout-minutes`, 30);
942
- }
943
- build?.addOverride("permissions", { contents: "read" });
944
- build?.addOverride("concurrency", {
945
- group: "${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}",
946
- "cancel-in-progress": true,
947
- });
948
- }
949
- }
950
-
951
- /**
952
- * Ignore each `codegen`-declaring package's `src/` from the root ESLint config.
885
+ * Ignore each codegen-declaring package's generated modules from root ESLint.
953
886
  * Those modules are read-only (ts-to-zod); lint `--fix` otherwise EACCES-crashes
954
- * on them. Runs in `preSynthesize` so mixin-added `codegen.inputs` are visible.
887
+ * on them. Runs in `preSynthesize` so mixin-added typed config is visible.
955
888
  */
956
889
  class EslintIgnoreCodegen extends Component {
957
890
  public override preSynthesize(): void {
@@ -959,16 +892,15 @@ class EslintIgnoreCodegen extends Component {
959
892
  if (!eslint) return;
960
893
  const rootAbs = resolve(this.project.outdir);
961
894
  for (const sub of this.project.subprojects) {
962
- if (!(sub instanceof javascript.NodeProject)) continue;
963
- const codegen = sub.package.manifest.codegen as { inputs?: string[] } | undefined;
964
- if (!codegen?.inputs?.length) continue;
895
+ if (!(sub instanceof DBXToolsTypeScriptProject)) continue;
896
+ if (sub.dbxToolsConfig.codegenInputs.length === 0) continue;
965
897
  const rel = toPosix(relative(rootAbs, sub.outdir));
966
898
  // Ignore the generated MODULES, not the package's whole `src/`. A codegen
967
899
  // package may hold hand-written modules next to its generated ones
968
900
  // (shared-genie generates `dashboards.ts` beside a hand-written
969
901
  // `genie-model.ts`), and a blanket `src/**` would silently stop linting
970
902
  // them - the failure mode being invisible, since ESLint just reports less.
971
- for (const module of codegenModulePaths(codegen.inputs)) {
903
+ for (const module of codegenModulePaths(sub.dbxToolsConfig.codegenInputs)) {
972
904
  eslint.addIgnorePattern(`${rel}/${module}`);
973
905
  }
974
906
  }
@@ -989,11 +921,10 @@ class PrettierIgnoreGenerated extends Component {
989
921
  prettier.addIgnorePattern("**/src/generated/**");
990
922
  const rootAbs = resolve(this.project.outdir);
991
923
  for (const sub of this.project.subprojects) {
992
- if (!(sub instanceof javascript.NodeProject)) continue;
924
+ if (!(sub instanceof DBXToolsTypeScriptProject)) continue;
993
925
  const rel = toPosix(relative(rootAbs, sub.outdir));
994
926
  prettier.addIgnorePattern(`${rel}/index.ts`);
995
- const codegen = sub.package.manifest.codegen as { inputs?: string[] } | undefined;
996
- for (const module of codegenModulePaths(codegen?.inputs ?? [])) {
927
+ for (const module of codegenModulePaths(sub.dbxToolsConfig.codegenInputs)) {
997
928
  prettier.addIgnorePattern(`${rel}/${module}`);
998
929
  }
999
930
  }
@@ -1109,7 +1040,6 @@ function resolveTags(p: DiscoveredPackage, tagPaths: Record<string, string[]>):
1109
1040
  /** Register the native projen tasks on the monorepo root. */
1110
1041
  function registerRootTasks(project: javascript.NodeProject): void {
1111
1042
  project.addTask("barrels", { execArgs: taskCommand("barrels.ts") });
1112
- project.addTask("openapi", { execArgs: taskCommand("openapi.ts") });
1113
1043
  project.addTask("clean", { execArgs: taskCommand("clean.ts"), receiveArgs: true });
1114
1044
  project.addTask("sync", {
1115
1045
  execArgs: taskCommand("sync.ts"),
@@ -1156,12 +1086,8 @@ function initProject(
1156
1086
  execArgs: [...packageStep.execArgs, "--ignore-scripts"],
1157
1087
  });
1158
1088
  }
1159
- // Only a ROOT configures the workspace; a child just swaps its default-laden
1160
- // `.gitignore` for a fresh one that carries package-specific patterns only.
1161
- swapChildGitignore(project, options);
1162
1089
  return;
1163
1090
  }
1164
- project.package.file.readonly = false;
1165
1091
 
1166
1092
  if (project instanceof DBXToolsNodeProject) {
1167
1093
  project.rootTsconfig = new DBXToolsRootTsconfig(project);
@@ -1177,7 +1103,6 @@ function initProject(
1177
1103
  // a plain `bun` exec rather than any wrapper: the default task is spawned by
1178
1104
  // nested installs/synths, and a wrapper that exported `npm_config_*` broke them.
1179
1105
  project.defaultTask?.reset("bun .projenrc.ts");
1180
- project.buildWorkflow?.workflow.file?.addOverride("jobs.build.env.BUN_VERSION", BUN_VERSION);
1181
1106
 
1182
1107
  // Pin bun's hoisted linker workspace-wide (see RootBunfigFile) so a peer dep
1183
1108
  // resolves to one copy and singletons/types stay coherent.
@@ -1189,6 +1114,9 @@ function initProject(
1189
1114
  if (selfDep) project.addDevDeps(selfDep);
1190
1115
  project.addDevDeps(...DEV_DEPS_ROOT);
1191
1116
  configureRootPackage(project);
1117
+ // Workspaces generated by this engine intentionally do not track local-registry
1118
+ // lockfiles, so the CI-named install task must resolve from manifests as well.
1119
+ project.package.installCiTask.reset("bun install");
1192
1120
  const roots = options.packageRoots ?? DEFAULT_PACKAGE_ROOTS;
1193
1121
  project.dbxToolsConfig.packageRoots = [...roots];
1194
1122
  if (options.syncResynthPaths?.length) {
@@ -1220,7 +1148,6 @@ function initProject(
1220
1148
  project.gitignore.addPatterns(".env", ".env.*", "!.env.example", "!.env.sample", ".idea/*");
1221
1149
  for (const root of roots) {
1222
1150
  project.annotateGenerated(`/${root}/**/index.ts`);
1223
- project.annotateGenerated(`/${root}/openapi/**`);
1224
1151
  project.annotateGenerated(`/${root}/**/src/generated/**`);
1225
1152
  }
1226
1153
  for (const member of project.extraWorkspaceMembers) {
@@ -1250,11 +1177,9 @@ function initProject(
1250
1177
  description: "Fix ESLint issues across the codebase",
1251
1178
  exec: "bun run eslint -- --fix",
1252
1179
  });
1253
- // Generated read-only outputs (barrels, openapi clients, app scripts, codegen).
1254
- // ESLint --fix cannot rewrite them; they are stamped by the barrel generator /
1255
- // openapi / codegen / projen.
1180
+ // Generated read-only outputs (barrels, app scripts, and codegen). ESLint
1181
+ // --fix cannot rewrite files owned by the barrel generator, codegen, or projen.
1256
1182
  for (const root of roots) {
1257
- eslint.addIgnorePattern(`${root}/openapi/**`);
1258
1183
  eslint.addIgnorePattern(`${root}/**/index.ts`);
1259
1184
  }
1260
1185
  eslint.addIgnorePattern("**/src/generated/**");
@@ -1266,14 +1191,11 @@ function initProject(
1266
1191
  // them to a project. ESLint still cannot parse them.
1267
1192
  eslint.addIgnorePattern("**/dev.ts");
1268
1193
  eslint.addIgnorePattern("**/build.ts");
1269
- // A deploy-staging helper that lives at a package root (outside any `src/**`
1270
- // tsconfig), same parse-resolution problem as the bun app scripts above.
1271
- eslint.addIgnorePattern("**/stage-deploy.ts");
1272
1194
  for (const override of BUN_APP_OVERRIDES) {
1273
1195
  eslint.addIgnorePattern(`**/${override}`);
1274
1196
  }
1275
- // Codegen packages declare `codegen.inputs` via mixins after construction; ignore
1276
- // their `src/` once manifests are known (preSynthesize), same reason as openapi.
1197
+ // Codegen inputs may be added by mixins after construction; ignore their
1198
+ // generated modules once typed package configuration is final.
1277
1199
  new EslintIgnoreCodegen(project);
1278
1200
  eslint.addRules({
1279
1201
  "import/no-relative-packages": "error",
@@ -1300,7 +1222,11 @@ function initProject(
1300
1222
  // cross-package imports.
1301
1223
  const tsResolver = eslint.config?.settings?.["import/resolver"]?.typescript;
1302
1224
  if (tsResolver) {
1303
- tsResolver.project = ["tsconfig.json", ...roots.map((r) => `${r}/**/tsconfig.json`)];
1225
+ tsResolver.project = [
1226
+ "tsconfig.json",
1227
+ ...roots.map((r) => `${r}/**/tsconfig.json`),
1228
+ ...project.extraWorkspaceMembers.map((member) => `${member}/tsconfig.json`),
1229
+ ];
1304
1230
  }
1305
1231
 
1306
1232
  const enabledTagMixins = resolveEnabledTagMixins(options.defaultTagMixins);
@@ -1334,10 +1260,13 @@ function initProject(
1334
1260
  continue;
1335
1261
  }
1336
1262
  const defaultPackageName = packageNameFor(project.scope, p.relPath, omitPrefixes);
1263
+ const packageOptions =
1264
+ options.resolvePackageOptions?.(p, { name: defaultPackageName }) ??
1265
+ ({ name: defaultPackageName } satisfies DiscoveredTypeScriptPackageOptions);
1337
1266
  new DBXToolsTypeScriptProject({
1338
1267
  parent: project,
1339
1268
  outdir: p.memberPath,
1340
- name: options.resolvePackageName?.(p, defaultPackageName) ?? defaultPackageName,
1269
+ ...packageOptions,
1341
1270
  tags,
1342
1271
  });
1343
1272
  }
@@ -1354,7 +1283,6 @@ function initProject(
1354
1283
  }
1355
1284
 
1356
1285
  new WorkspaceValidationTasks(project);
1357
- new WorkflowDefaults(project);
1358
1286
  new PrettierIgnoreGenerated(project);
1359
1287
  new GeneratedSource(project);
1360
1288
 
@@ -1368,56 +1296,11 @@ function initProject(
1368
1296
  pythonRoot: options.releasePythonRoot,
1369
1297
  validationTasks: options.releaseValidationTasks,
1370
1298
  setupSteps: options.releaseSetupSteps,
1299
+ synthesisCommands: options.releaseSynthesisCommands,
1371
1300
  });
1372
1301
  }
1373
1302
  }
1374
1303
 
1375
- /**
1376
- * A child's `.gitignore`, tracking whether any pattern was ever added so an
1377
- * untouched (empty) file can be dropped at presynth. `exclude`/`include` and
1378
- * constructor `ignorePatterns` all funnel through {@link addPatterns}, so the flag
1379
- * sees every route - but seed patterns must be added AFTER construction (see
1380
- * {@link swapChildGitignore}) because class fields initialize after `super()`.
1381
- */
1382
- class ChildGitignore extends IgnoreFile {
1383
- /** True once any pattern landed (custom patterns => the file is emitted). */
1384
- public hasPatterns = false;
1385
-
1386
- public override addPatterns(...patterns: string[]): void {
1387
- if (patterns.length) this.hasPatterns = true;
1388
- super.addPatterns(...patterns);
1389
- }
1390
- }
1391
-
1392
- /**
1393
- * Swap a CHILD's default `.gitignore` - pre-populated by `NodeProject` with the
1394
- * same defaults the root already carries (git applies the root's file to the whole
1395
- * tree) - for a FRESH {@link ChildGitignore}. Caller-supplied patterns
1396
- * (`gitignore` / `gitIgnoreOptions.ignorePatterns`) are re-seeded, and later
1397
- * `project.gitignore.addPatterns(...)` calls (tag/user mixins) land here too, so a
1398
- * package CAN carry package-specific ignores without inheriting the root noise.
1399
- * Left empty, the file is dropped by {@link preSynthesizeProject}. Safe because
1400
- * projen only writes gitignore defaults at construction time (`addDefaultGitIgnore`,
1401
- * yarn-berry config), never during synth.
1402
- */
1403
- function swapChildGitignore(
1404
- project: javascript.NodeProject,
1405
- options: DBXToolsJavaScriptProjectOptions,
1406
- ): void {
1407
- project.tryRemoveFile(".gitignore");
1408
- const fresh = new ChildGitignore(project, ".gitignore", {
1409
- ...options.gitIgnoreOptions,
1410
- // Re-added below so the custom-pattern flag sees them (not clobbered by the
1411
- // subclass field initializer running after super()).
1412
- ignorePatterns: undefined,
1413
- });
1414
- const seeds = [...(options.gitignore ?? []), ...(options.gitIgnoreOptions?.ignorePatterns ?? [])];
1415
- if (seeds.length) fresh.addPatterns(...seeds);
1416
- // `Project.gitignore` is readonly only at compile time; rebind it so every
1417
- // subsequent `project.gitignore.*` call reaches the fresh file.
1418
- (project as { gitignore: IgnoreFile }).gitignore = fresh;
1419
- }
1420
-
1421
1304
  function preSynthesizeProject(project: javascript.NodeProject): void {
1422
1305
  // `Project.files` is OWN-project only (its `components` getter filters on the
1423
1306
  // project's own node path), so reaching a child's files means walking the tree.
@@ -1439,19 +1322,5 @@ function preSynthesizeProject(project: javascript.NodeProject): void {
1439
1322
  // only. Runs here rather than in the constructor so the tags have already
1440
1323
  // installed their `exports` layouts for it to mirror.
1441
1324
  if (p instanceof javascript.NodeProject) applyCompiledPublish(p);
1442
- // A child's `.gitignore` survives ONLY when it carries custom patterns (see
1443
- // swapChildGitignore). `.gitattributes` is always dropped - the root's
1444
- // annotateGenerated globs cover the children. Runs once from the root's
1445
- // preSynthesize and again from each child's own; both passes agree, so the
1446
- // second is a no-op.
1447
- const keepGitignore = p.gitignore instanceof ChildGitignore && p.gitignore.hasPatterns;
1448
- for (const path of keepGitignore ? [".gitattributes"] : [".gitignore", ".gitattributes"]) {
1449
- if (p.tryRemoveFile(path)) {
1450
- const rootPath = resolve(p.outdir, path);
1451
- if (existsSync(rootPath)) {
1452
- p.logger.info(`Removed ${rootPath} from ${p.name}`);
1453
- }
1454
- }
1455
- }
1456
1325
  }
1457
1326
  }
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Composable, runtime-safe predicates for selecting Projen projects by type,
3
+ * package identity, tags, and repository path.
4
+ */
1
5
  import { relative } from "node:path";
2
6
  import { match, PathMatchInput, PathMatchPredicate } from "@dbx-tools/path";
3
7
  import {
@@ -19,9 +23,9 @@ import type { DBXToolsProject } from "./project.ts";
19
23
  *
20
24
  * Uses projen's own `Project.isProject` rather than `instanceof`. It tests for
21
25
  * `Symbol.for("projen.Project")`, which every `Project` constructor stamps on
22
- * itself, so it still matches when a construct came from a SECOND resolved copy
23
- * of projen - the engine pins its own `projen` dependency separately from the
24
- * consuming root's, which is exactly the case `instanceof` fails silently.
26
+ * itself, so it still matches if an invalid consumer install resolves a second
27
+ * Projen copy despite the engine's peer dependency. An `instanceof` guard would
28
+ * silently reject that construct before the install problem can be diagnosed.
25
29
  */
26
30
  export function isProject(): Predicate<IConstruct, Project> {
27
31
  return predicate.create((c: IConstruct): c is Project => Project.isProject(c));