@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/codegen.ts CHANGED
@@ -1,20 +1,20 @@
1
1
  /**
2
2
  * Codegen generator (ts-to-zod based).
3
3
  *
4
- * Scans every package whose `package.json` declares a `codegen`
5
- * field and turns the listed upstream `.d.ts` inputs into read-only `src/`
4
+ * Scans every package whose `dbxToolsConfig.codegenInputs` declares upstream
5
+ * `.d.ts` inputs and turns them into read-only `src/`
6
6
  * modules of zod schemas plus matching inferred TypeScript types. Each input
7
7
  * emits one `src/<name>.ts` (schemas + `export type X = z.infer<typeof
8
8
  * xSchema>` lines); the barrel generator then namespaces it into the package's
9
9
  * root barrel like any other `src/` module (`sdkModel.dashboards.genieMessageSchema`).
10
10
  *
11
11
  * The single source of truth for "which packages get generated content, from
12
- * which inputs" is each consumer's own `package.json`:
12
+ * which inputs" is the typed config serialized into each package manifest:
13
13
  *
14
14
  * {
15
15
  * "name": "@dbx-tools/shared-sdk-model",
16
- * "codegen": {
17
- * "inputs": [
16
+ * "dbxToolsConfig": {
17
+ * "codegenInputs": [
18
18
  * "node_modules/@databricks/sdk-experimental/dist/apis/dashboards/model.d.ts"
19
19
  * ]
20
20
  * }
@@ -46,18 +46,23 @@
46
46
  import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
47
47
  import { createRequire } from "node:module";
48
48
  import { basename, dirname, join, resolve } from "node:path";
49
- import { log, object } from "@dbx-tools/shared-core";
49
+ import { log } 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, makeReadonly, makeWritable } from "./generated.ts";
53
- import { readPackageManifest, recordedPackages, resolveRepoRoot } from "./packages.ts";
53
+ import {
54
+ readDbxToolsConfig,
55
+ readPackageManifest,
56
+ recordedPackages,
57
+ resolveRepoRoot,
58
+ } from "./packages.ts";
54
59
 
55
60
  const logger = log.logger("projen:codegen");
56
61
 
57
62
  /** Do-not-edit banner stamped on every generated codegen module. */
58
63
  const HEADER = header({
59
64
  tool: "projen synth (codegen: ts-to-zod)",
60
- source: "the upstream .d.ts declared in package.json codegen.inputs",
65
+ source: "the upstream .d.ts declared in dbxToolsConfig.codegenInputs",
61
66
  });
62
67
 
63
68
  interface CodegenInput {
@@ -77,11 +82,9 @@ function sourceLabel(source: string): string {
77
82
  return source.startsWith(nodeModulesPrefix) ? source.slice(nodeModulesPrefix.length) : source;
78
83
  }
79
84
 
80
- /** Read a package's `package.json` `codegen.inputs`, or `undefined` if absent. */
85
+ /** Read a package's serialized `dbxToolsConfig.codegenInputs`, or `undefined` if absent. */
81
86
  function codegenInputs(dir: string): string[] | undefined {
82
- const codegen = readPackageManifest(dir)?.codegen;
83
- if (!object.isRecord(codegen)) return undefined;
84
- const inputs = codegen.inputs;
87
+ const inputs = readDbxToolsConfig(dir)?.codegenInputs;
85
88
  return Array.isArray(inputs) && inputs.length > 0 ? (inputs as string[]) : undefined;
86
89
  }
87
90
 
@@ -100,7 +103,7 @@ function parseInputArg(value: string): CodegenInput {
100
103
  }
101
104
 
102
105
  /**
103
- * The `src/` modules a set of `codegen.inputs` specs will emit, as package-relative
106
+ * The `src/` modules a set of codegen input specs will emit, as package-relative
104
107
  * posix paths - WITHOUT resolving or reading any input.
105
108
  *
106
109
  * This exists so callers that only need to know WHICH files are generated (the
@@ -316,9 +319,9 @@ function generatePackage(
316
319
  const sourceText = preprocess(stripImports(tsRuntime, sourcePath));
317
320
  const { getZodSchemasFile, getInferredTypes, errors } = generate({
318
321
  sourceText,
319
- // Carry JSDoc through so the `@description` tag stays visible alongside
320
- // the matching `.describe(...)`.
321
- keepComments: true,
322
+ // JSDoc is rewritten to `@description` so ts-to-zod emits `.describe()`.
323
+ // Keep the generated module comment-free; Zod owns the prose.
324
+ keepComments: false,
322
325
  });
323
326
  if (errors.length) {
324
327
  for (const err of errors) logger.warn(`${destination} from ${source}: ${err}`);
@@ -344,9 +347,9 @@ function generatePackage(
344
347
  }
345
348
 
346
349
  /**
347
- * Regenerate the `generated/` tree for every package declaring a
348
- * `codegen` field. Returns the package dirs it wrote so the caller can rebuild
349
- * their barrels. `ts-to-zod` + `typescript` are lazy-loaded.
350
+ * Regenerate source modules for every package declaring `dbxToolsConfig.codegenInputs`.
351
+ * Returns the package dirs it wrote so the caller can rebuild their barrels.
352
+ * `ts-to-zod` and `typescript` are lazy-loaded.
350
353
  */
351
354
  export function generateCodegen(
352
355
  projectRoot: string = resolveRepoRoot(),
@@ -365,7 +368,7 @@ export function generateCodegen(
365
368
  // than printing a line about work that was never asked for. `debug` keeps it
366
369
  // reachable when someone is actually chasing why codegen produced nothing.
367
370
  if (targets.length === 0) {
368
- logger.debug("no packages declare a `codegen` field");
371
+ logger.debug("no packages declare dbxToolsConfig.codegenInputs");
369
372
  return [];
370
373
  }
371
374
 
@@ -1,25 +1,62 @@
1
1
  /** Typed `package.json` `dbxToolsConfig` record for a package. */
2
2
  import { javascript } from "projen";
3
+ import { z } from "zod";
3
4
 
4
5
  /** `package.json` field name for the dbx-tools config object. */
5
- const DBX_TOOLS_CONFIG_KEY = "dbxToolsConfig";
6
+ const CONFIG_KEY = "dbxToolsConfig";
6
7
 
7
8
  /** Options for {@link DBXToolsConfig}. */
8
9
  export interface DBXToolsConfigOptions {
9
10
  /** Initial tags to record (distinct; order preserved). */
10
11
  readonly tags?: string[];
12
+ /** Declaration inputs converted to generated Zod modules (distinct; order preserved). */
13
+ readonly codegenInputs?: string[];
14
+ /** Whether the package receives compiled npm publication machinery. */
15
+ readonly publishable?: boolean;
11
16
  }
12
17
 
13
- interface DBXToolsConfigData {
14
- readonly tags?: string[];
15
- readonly packageRoots?: string[];
16
- readonly syncResynthPaths?: string[];
17
- }
18
+ /** Serialized dbx-tools package and workspace generator configuration. */
19
+ export const DBXToolsConfigDataSchema = z
20
+ .object({
21
+ tags: z.array(z.string().min(1)).readonly().optional().describe("Package runtime tags."),
22
+ codegenInputs: z
23
+ .array(z.string().min(1))
24
+ .readonly()
25
+ .optional()
26
+ .describe("Declaration inputs converted to generated Zod modules."),
27
+ packageRoots: z
28
+ .array(z.string().min(1))
29
+ .readonly()
30
+ .optional()
31
+ .describe("Repository paths scanned for JavaScript packages."),
32
+ publishable: z
33
+ .boolean()
34
+ .optional()
35
+ .describe("Whether compiled npm publication machinery is enabled."),
36
+ syncResynthPaths: z
37
+ .array(z.string().min(1))
38
+ .readonly()
39
+ .optional()
40
+ .describe("Additional paths that trigger watcher resynthesis."),
41
+ })
42
+ .strict()
43
+ .readonly()
44
+ .describe("dbxToolsConfig serialized in a generated package manifest.");
45
+
46
+ /** Serialized dbx-tools package and workspace generator configuration. */
47
+ export type DBXToolsConfigData = z.infer<typeof DBXToolsConfigDataSchema>;
18
48
 
19
49
  /** Owns the typed in-memory `dbxToolsConfig` written to `package.json`. */
20
50
  export class DBXToolsConfig {
51
+ /** Runtime tags that select package mixins. */
21
52
  readonly tags: string[];
53
+ /** Declaration inputs converted to generated Zod modules. */
54
+ readonly codegenInputs: string[];
55
+ /** Repository paths scanned for JavaScript package discovery. */
22
56
  packageRoots?: string[];
57
+ /** Whether compiled npm publication machinery is enabled. */
58
+ publishable: boolean;
59
+ /** Additional repository paths that trigger watcher resynthesis. */
23
60
  syncResynthPaths?: string[];
24
61
 
25
62
  constructor(
@@ -27,18 +64,24 @@ export class DBXToolsConfig {
27
64
  options: DBXToolsConfigOptions = {},
28
65
  ) {
29
66
  this.tags = [...(options.tags ?? [])];
30
- project.package.addField(DBX_TOOLS_CONFIG_KEY, () => {
67
+ this.codegenInputs = [...(options.codegenInputs ?? [])];
68
+ this.publishable = options.publishable ?? true;
69
+ project.package.addField(CONFIG_KEY, () => {
31
70
  const data = this.data();
32
71
  return Object.keys(data).length === 0 ? undefined : data;
33
72
  });
34
73
  }
35
74
 
75
+ /** Render the serializable package manifest record. */
36
76
  data(): DBXToolsConfigData {
37
77
  const tags = [...new Set(this.tags)];
38
- return {
78
+ const codegenInputs = [...new Set(this.codegenInputs)];
79
+ return DBXToolsConfigDataSchema.parse({
39
80
  ...(tags.length ? { tags } : {}),
81
+ ...(codegenInputs.length ? { codegenInputs } : {}),
82
+ ...(!this.publishable ? { publishable: false } : {}),
40
83
  ...(this.packageRoots?.length ? { packageRoots: this.packageRoots } : {}),
41
84
  ...(this.syncResynthPaths?.length ? { syncResynthPaths: this.syncResynthPaths } : {}),
42
- };
85
+ });
43
86
  }
44
87
  }
package/src/generated.ts CHANGED
@@ -1,14 +1,14 @@
1
1
  /**
2
2
  * Read-only / do-not-edit helpers for generated output projen does not own as a
3
- * native `FileBase`: the barrels we write, openapi artifacts, and any other
4
- * toolchain file stamped via {@link stampGenerated}.
3
+ * native `FileBase`: package barrels, codegen modules, PythonMonkey bundles,
4
+ * and any other toolchain file stamped via {@link stampGenerated}.
5
5
  *
6
6
  * projen already writes its own generated files read-only with a marker; these give
7
7
  * barrels and other watch-time output the same contract - a do-not-edit header and a
8
8
  * read-only (0o444) bit. Rewriting one therefore goes through {@link makeWritable}
9
9
  * first, then {@link stampGenerated}.
10
10
  */
11
- import { chmodSync, existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
11
+ import { chmodSync, existsSync, readFileSync, writeFileSync } from "node:fs";
12
12
 
13
13
  /** First line of every banner {@link header} builds. */
14
14
  const MARKER = "// GENERATED by";
@@ -26,20 +26,7 @@ export function makeReadonly(file: string): void {
26
26
  if (existsSync(file)) chmodSync(file, READONLY);
27
27
  }
28
28
 
29
- /**
30
- * True if the file exists and has no owner-write bit. This is the toolchain's
31
- * generated-file signal: everything projen and the barrel generator write is set read-only
32
- * (see {@link makeReadonly}), while hand-authored source stays writable - so a
33
- * read-only file under the repo (outside vendor/build dirs) is a generated file.
34
- */
35
- export function isReadonly(file: string): boolean {
36
- try {
37
- return (statSync(file).mode & 0o200) === 0;
38
- } catch {
39
- return false;
40
- }
41
- }
42
-
29
+ /** Inputs for the durable generated-file banner. */
43
30
  export interface HeaderOpts {
44
31
  /** What produced the file, e.g. `"projen watch"`. */
45
32
  readonly tool: string;
package/src/mixin.ts CHANGED
@@ -38,10 +38,6 @@ export function create(
38
38
  ): ConstructsMixin {
39
39
  return {
40
40
  supports,
41
- applyTo: (construct: IConstruct): void => {
42
- if (supports(construct)) {
43
- (applyTo as (construct: IConstruct) => void)(construct);
44
- }
45
- },
41
+ applyTo: applyTo as (construct: IConstruct) => void,
46
42
  };
47
43
  }
package/src/packages.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * Package discovery + shared filesystem helpers.
3
3
  *
4
4
  * Terminology (Bit-style): a **tag** names a target environment
5
- * (React/Vite, Node, agnostic, ...); a workspace **package** is a folder with a
5
+ * (React/Bun, Node, agnostic, ...); a workspace **package** is a folder with a
6
6
  * `src/` holding at least one module file (`.ts`/`.tsx`/`.js`/`.jsx`). "Scope" is
7
7
  * reserved for the npm `@scope/` in package identifiers (e.g. `@dbx-tools/ui-app`).
8
8
  *
@@ -20,7 +20,7 @@
20
20
  * path relative to the root, reading NO manifest. {@link recordedPackages} reads
21
21
  * the recorded members from `pnpm-workspace.yaml` - the SOURCE OF TRUTH - and
22
22
  * augments each with the `name` and `tags` read back from its own `package.json`
23
- * (post-synth: barrels, watch, openapi), which is authoritative and so reflects any
23
+ * (post-synth: barrels and watch), which is authoritative and so reflects any
24
24
  * synth-time name override or resolved tag set.
25
25
  */
26
26
  import { existsSync, readFileSync } from "node:fs";
@@ -29,6 +29,7 @@ import * as projectUtils from "@dbx-tools/core/project-utils";
29
29
  import { find } from "@dbx-tools/path";
30
30
  import { json, object, stringUtils } from "@dbx-tools/shared-core";
31
31
  import { parse } from "yaml";
32
+ import { DBXToolsConfigDataSchema, type DBXToolsConfigData } from "./dbx-tools-config.ts";
32
33
  import { isGenerated } from "./generated.ts";
33
34
 
34
35
  /** Resolve the nearest package/projenrc root at call time. */
@@ -255,16 +256,18 @@ export function readPackageManifest(dir: string): Record<string, unknown> | unde
255
256
  }
256
257
  }
257
258
 
258
- /** A package's `dbxToolsConfig` object, or `undefined` when absent. */
259
- export function readDbxToolsConfig(dir: string): Record<string, unknown> | undefined {
259
+ /** A package's validated `dbxToolsConfig` object, or `undefined` when absent. */
260
+ export function readDbxToolsConfig(dir: string): DBXToolsConfigData | undefined {
260
261
  const config = readPackageManifest(dir)?.dbxToolsConfig;
261
- return object.isRecord(config) ? config : undefined;
262
+ if (config === undefined) return undefined;
263
+ const parsed = DBXToolsConfigDataSchema.safeParse(config);
264
+ return parsed.success ? parsed.data : undefined;
262
265
  }
263
266
 
264
267
  /** Read `<dir>/package.json`'s `dbxToolsConfig.tags` (`undefined` if absent). */
265
268
  function readManifestTags(dir: string): string[] | undefined {
266
269
  const tags = readDbxToolsConfig(dir)?.tags;
267
- return Array.isArray(tags) ? (tags as string[]) : undefined;
270
+ return tags ? [...tags] : undefined;
268
271
  }
269
272
 
270
273
  /**
@@ -280,7 +283,7 @@ export function syncResynthPaths(projectRoot: string = resolveRepoRoot()): strin
280
283
  /**
281
284
  * The recorded workspace members from `pnpm-workspace.yaml` (the source of truth),
282
285
  * each augmented with the `tags` read back from its `package.json`. This is what
283
- * every post-synth command (barrels, watch, openapi) uses: the manifest is
286
+ * every post-synth command (barrels and watch) uses: the manifest is
284
287
  * authoritative, so it reflects the resolved tag set.
285
288
  * Sorted by path.
286
289
  */
@@ -333,18 +336,18 @@ export function workspaceDependencyDirectories(
333
336
  }
334
337
 
335
338
  /**
336
- * The configured roots recorded in the root manifest. Older workspaces without
337
- * that field fall back to the distinct first segment of every recorded member,
338
- * unioned with the defaults.
339
+ * The configured package roots plus the first segment of every recorded member.
340
+ * The member union includes self-synthesizing extra workspace members; older
341
+ * manifests without configured roots fall back to {@link DEFAULT_PACKAGE_ROOTS}.
339
342
  */
340
343
  export function recordedRoots(projectRoot: string = resolveRepoRoot()): string[] {
341
344
  const configured = readDbxToolsConfig(projectRoot)?.packageRoots;
342
- if (Array.isArray(configured)) {
343
- const roots = stringUtils.parseList(configured.map((root) => String(root)));
344
- if (roots.length) return roots;
345
- }
346
- const roots = new Set<string>(DEFAULT_PACKAGE_ROOTS);
345
+ const configuredRoots = Array.isArray(configured)
346
+ ? stringUtils.parseList(configured.map((root) => String(root)))
347
+ : [];
348
+ const roots = new Set<string>(configuredRoots.length ? configuredRoots : DEFAULT_PACKAGE_ROOTS);
347
349
  for (const member of readRecordedMembers(projectRoot)) {
350
+ if (configuredRoots.some((root) => member === root || member.startsWith(`${root}/`))) continue;
348
351
  const pkg = packageOfMember(projectRoot, member);
349
352
  if (pkg) roots.add(pkg.root);
350
353
  }
@@ -76,9 +76,6 @@ const DEFAULT_CATALOG: Catalog = {
76
76
  typescript: "^5.9.3",
77
77
  commander: "^15.0.0",
78
78
  "@clack/prompts": "^1.7.0",
79
- "openapi-fetch": "^0.17.0",
80
- tsoa: "^6.6.0",
81
- concurrently: "^10.0.3",
82
79
  "@databricks/appkit": "0.81.0",
83
80
  "@databricks/appkit-ui": "0.81.0",
84
81
  "@databricks/sdk-experimental": "^0.17.0",
@@ -141,7 +138,7 @@ const DEFAULT_WORKSPACE_YAML: javascript.PnpmWorkspaceYamlOptions = {
141
138
  export interface DBXToolsPNPMWorkspaceOptions {
142
139
  /** Initial `catalog:` registry. Defaults to {@link DEFAULT_CATALOG}. */
143
140
  readonly catalog?: Catalog;
144
- /** Initial build allowances, merged over `{ esbuild: true }`. */
141
+ /** Initial build allowances merged over the engine's reviewed defaults. */
145
142
  readonly allowBuilds?: AllowBuilds;
146
143
  }
147
144