@dbx-tools/projen 0.6.60 → 0.6.62

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
@@ -124,35 +124,6 @@ package is attempted even if one fails; the failures are re-thrown together as a
124
124
  `AggregateError` naming each package, rather than the first one abandoning the
125
125
  rest of the sweep.
126
126
 
127
- ### Generate Node And Python From Rosetta Sources
128
-
129
- The experimental `rs-packages` convention keeps TypeScript as the canonical
130
- Node implementation while colocating an explicit Python implementation in the
131
- same file:
132
-
133
- ```ts
134
- export function capitalize(value: string): string {
135
- return value ? value[0]!.toUpperCase() + value.slice(1) : value;
136
- }
137
-
138
- /* @rs-python
139
- def capitalize(value: str) -> str:
140
- return value[:1].upper() + value[1:] if value else value
141
- @rs-end */
142
- ```
143
-
144
- By default, `rs-packages/shared/core/src/string.ts` generates:
145
-
146
- - `packages/shared/core/src/string.ts`
147
- - `python-packages/shared-core/src/shared_core/string.py`
148
-
149
- Use `// @rs-node <path>` or `// @rs-python-path <path>` only when the inferred
150
- paths are unsuitable. Every Rosetta source must live below a package's `src/`
151
- folder and include at least one Python block; generation fails rather than
152
- silently emitting an empty Python module. `DBXToolsNodeProject` enables the
153
- `rs-packages` root by default, accepts `rsPackageRoots` for custom roots, and
154
- accepts `rsPackageRoots: false` to disable the experiment.
155
-
156
127
  ## Generate OpenAPI Clients
157
128
 
158
129
  ```ts
@@ -217,7 +188,6 @@ file contract as the CLI.
217
188
  - `pnpmWorkspace` - generated pnpm workspace file and catalog model.
218
189
  - `barrels` / `moduleExports` - public entrypoint generation.
219
190
  - `codegen` - `.d.ts` to zod schema generation.
220
- - `rsPackages` - annotated TypeScript source to Node/Python generation.
221
191
  - `openapi` - tsoa/OpenAPI package generation.
222
192
  - `bunApp` / `tsconfig` / `vscode` - generated support files/components.
223
193
  - `generated` / `clean` / `watch` / `scaffold` - read-only file stamping,
@@ -226,8 +196,8 @@ file contract as the CLI.
226
196
  - `engineRoot` - engine package root resolution for bootstrapped repos.
227
197
 
228
198
  The engine registers its commands as projen tasks on the workspace root, so run
229
- them with `bun run <task>` - `sync` (add `--watch`), `barrels`, `openapi`,
230
- `rs-packages` (also supports `--watch`), and `clean`.
199
+ them with `bun run <task>` - `sync` (add `--watch`), `barrels`, `openapi`, and
200
+ `clean`.
231
201
  [`@dbx-tools/cli`](../packages/cli/dbx-tools) is only needed to
232
202
  bootstrap a folder that has no `.projenrc.ts` or toolchain yet.
233
203
 
@@ -237,15 +207,14 @@ Every repo-wide task lives on the root, and the root's `compile` / `test`
237
207
  delegate with `bun run --filter '*'` rather than emitting a step per member - so
238
208
  a new package is covered without a re-synth. Work from the root:
239
209
 
240
- | Task | What it does |
241
- | --------------------- | -------------------------------------------------- |
242
- | `bun run build` | `compile` + `test` + `package` across every member |
243
- | `bun run compile` | `tsc --build` in each member, in parallel |
244
- | `bun run test` | `eslint` once, then each member's tests |
245
- | `bun run sync` | re-synth (`--watch` to keep synthing) |
246
- | `bun run barrels` | regenerate the read-only `index.ts` barrels |
247
- | `bun run rs-packages` | regenerate Node/Python Rosetta outputs |
248
- | `bun run bump` | version, tag, and publish |
210
+ | Task | What it does |
211
+ | ----------------- | -------------------------------------------------- |
212
+ | `bun run build` | `compile` + `test` + `package` across every member |
213
+ | `bun run compile` | `tsc --build` in each member, in parallel |
214
+ | `bun run test` | `eslint` once, then each member's tests |
215
+ | `bun run sync` | re-synth (`--watch` to keep synthing) |
216
+ | `bun run barrels` | regenerate the read-only `index.ts` barrels |
217
+ | `bun run bump` | version, tag, and publish |
249
218
 
250
219
  Members intentionally keep only the tasks that something OTHER than a human
251
220
  invokes, so there is no second place to run the same thing:
package/index.ts CHANGED
@@ -18,13 +18,20 @@ export * as project from "./src/project.ts";
18
18
  export * as projectPredicate from "./src/project-predicate.ts";
19
19
  export * as publish from "./src/publish.ts";
20
20
  export * as release from "./src/release.ts";
21
- export * as rsPackages from "./src/rs-packages.ts";
22
21
  export * as scaffold from "./src/scaffold.ts";
23
22
  export * as tags from "./src/tags.ts";
24
23
  export * as tsconfig from "./src/tsconfig.ts";
25
24
  export * as vscode from "./src/vscode.ts";
26
25
  export * as watch from "./src/watch.ts";
27
- export { BUN_DEV_OVERRIDE, BUN_BUILD_OVERRIDE, BUN_APP_OVERRIDES, RootBunfigFile, BunfigFile, BunDevServerFile, BunBuildFile } from "./src/bun-app.ts";
26
+ export {
27
+ BUN_DEV_OVERRIDE,
28
+ BUN_BUILD_OVERRIDE,
29
+ BUN_APP_OVERRIDES,
30
+ RootBunfigFile,
31
+ BunfigFile,
32
+ BunDevServerFile,
33
+ BunBuildFile,
34
+ } from "./src/bun-app.ts";
28
35
  export { DBXToolsConfig } from "./src/dbx-tools-config.ts";
29
36
  export type { DBXToolsConfigOptions } from "./src/dbx-tools-config.ts";
30
37
  export { resolvePkgRoot } from "./src/engine-root.ts";
@@ -35,13 +42,21 @@ export { repoRoot, DEFAULT_PACKAGE_ROOTS, DiscoveredPackage } from "./src/packag
35
42
  export type { RecordedPackage } from "./src/packages.ts";
36
43
  export { PnpmWorkspaceState } from "./src/pnpm-workspace.ts";
37
44
  export type { Catalog, AllowBuilds, DBXToolsPNPMWorkspaceOptions } from "./src/pnpm-workspace.ts";
38
- export { PackageIdentifier, PROJEN_VERSION, DBXToolsNodeProject, DBXToolsTypeScriptProject } from "./src/project.ts";
39
- export type { DBXToolsProject, DBXToolsProjectOptions, DBXToolsTypeScriptProjectOptions, ApplyToProjectsOptions } from "./src/project.ts";
45
+ export {
46
+ PackageIdentifier,
47
+ PROJEN_VERSION,
48
+ DBXToolsNodeProject,
49
+ DBXToolsTypeScriptProject,
50
+ } from "./src/project.ts";
51
+ export type {
52
+ DBXToolsProject,
53
+ DBXToolsProjectOptions,
54
+ DBXToolsTypeScriptProjectOptions,
55
+ ApplyToProjectsOptions,
56
+ } from "./src/project.ts";
40
57
  export { COMPILED_DIR, COMPILED_COMPILER_OPTIONS } from "./src/publish.ts";
41
58
  export { DBXToolsRelease } from "./src/release.ts";
42
59
  export type { StandaloneRelease, DBXToolsReleaseOptions } from "./src/release.ts";
43
- export { DEFAULT_RS_PACKAGE_ROOTS, RsPackages } from "./src/rs-packages.ts";
44
- export type { RsPackageOutput, GenerateRsPackagesOptions } from "./src/rs-packages.ts";
45
60
  export { AGNOSTIC_COMPILER_OPTIONS, PACKAGE_TAG_MIXINS } from "./src/tags.ts";
46
61
  export type { PackageTag } from "./src/tags.ts";
47
62
  export { DBXToolsRootTsconfig } from "./src/tsconfig.ts";
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.60",
30
- "@dbx-tools/path": "0.6.60",
31
- "@dbx-tools/shared-core": "0.6.60",
29
+ "@dbx-tools/core": "0.6.62",
30
+ "@dbx-tools/path": "0.6.62",
31
+ "@dbx-tools/shared-core": "0.6.62",
32
32
  "commander": "^15.0.0",
33
33
  "concurrently": "^10.0.3",
34
34
  "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.60",
50
+ "version": "0.6.62",
51
51
  "types": "index.ts",
52
52
  "type": "module",
53
53
  "exports": {
package/src/codegen.ts CHANGED
@@ -46,10 +46,10 @@
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
50
  import type * as ts from "typescript";
50
51
  import { lazyRequire } from "./_lazy-require.ts";
51
52
  import { header, isReadonly, makeReadonly, makeWritable } from "./generated.ts";
52
- import { log, object } from "@dbx-tools/shared-core";
53
53
  import { readPackageManifest, repoRoot, recordedPackages } from "./packages.ts";
54
54
 
55
55
  const logger = log.logger("projen:codegen");
@@ -31,11 +31,12 @@ function readDBXToolsConfig(pkg: javascript.NodePackage): Record<string, unknown
31
31
 
32
32
  function loadConfig(dbxToolsConfig: DBXToolsConfig, pkg: javascript.NodePackage) {
33
33
  const { tags, ...config } = readDBXToolsConfig(pkg);
34
- if (Array.isArray(tags))
34
+ if (Array.isArray(tags)) {
35
35
  object
36
36
  .sequence(tags)
37
37
  .filter((v: unknown) => typeof v === "string")
38
38
  .forEach((v: string) => dbxToolsConfig.tags.push(v));
39
+ }
39
40
  Object.entries(config).forEach(([key, value]) => {
40
41
  dbxToolsConfig[key] = value;
41
42
  });
package/src/openapi.ts CHANGED
@@ -23,11 +23,11 @@
23
23
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
24
24
  import { createRequire } from "node:module";
25
25
  import { join } from "node:path";
26
- import type * as ts from "typescript";
27
26
  import { find } from "@dbx-tools/path";
27
+ import { log } from "@dbx-tools/shared-core";
28
+ import type * as ts from "typescript";
28
29
  import { lazyRequire } from "./_lazy-require.ts";
29
30
  import { makeReadonly, makeWritable, stampGenerated } from "./generated.ts";
30
- import { log } from "@dbx-tools/shared-core";
31
31
  import {
32
32
  type RecordedPackage,
33
33
  isModuleFile,
package/src/packages.ts CHANGED
@@ -26,8 +26,8 @@
26
26
  import { existsSync, readFileSync } from "node:fs";
27
27
  import { extname, relative, resolve, sep } from "node:path";
28
28
  import { project as coreProject } from "@dbx-tools/core";
29
- import { json, object, string } from "@dbx-tools/shared-core";
30
29
  import { find } from "@dbx-tools/path";
30
+ import { json, object, string } from "@dbx-tools/shared-core";
31
31
  import { parse } from "yaml";
32
32
 
33
33
  /**
@@ -1,3 +1,5 @@
1
+ import { relative } from "node:path";
2
+ import { match, PathMatchInput, PathMatchPredicate } from "@dbx-tools/path";
1
3
  import {
2
4
  object,
3
5
  predicate,
@@ -7,11 +9,9 @@ import {
7
9
  } from "@dbx-tools/shared-core";
8
10
  import { IConstruct } from "constructs";
9
11
  import { Project } from "projen";
10
- import { DBXToolsProject, DBXToolsNodeProject, DBXToolsTypeScriptProject } from "./project.ts";
11
- import { toPosix } from "./packages.ts";
12
- import { relative } from "node:path";
13
- import { match, PathMatchInput, PathMatchPredicate } from "@dbx-tools/path";
14
12
  import { project } from "..";
13
+ import { toPosix } from "./packages.ts";
14
+ import { DBXToolsProject, DBXToolsNodeProject, DBXToolsTypeScriptProject } from "./project.ts";
15
15
 
16
16
  /**
17
17
  * Guard: the construct is a projen {@link Project} - the base every builder here
package/src/project.ts CHANGED
@@ -6,22 +6,16 @@
6
6
  * {@link DBXToolsNodeProject} (monorepo root) and {@link DBXToolsTypeScriptProject}
7
7
  * (a package, or a standalone compiling root) both implement {@link DBXToolsProject}.
8
8
  */
9
- import { object, string, type OneOrMany } from "@dbx-tools/shared-core";
10
- import { ignore, match, PathMatchInput } from "@dbx-tools/path";
11
- import { project as coreProject } from "@dbx-tools/core";
12
9
  import { existsSync, readdirSync } from "node:fs";
13
10
  import { dirname, join, relative, resolve } from "node:path";
11
+ import { project as coreProject } from "@dbx-tools/core";
12
+ import { ignore, match, PathMatchInput } from "@dbx-tools/path";
13
+ import { object, string, type OneOrMany } from "@dbx-tools/shared-core";
14
+ import { IConstruct } from "constructs";
14
15
  import { Component, IgnoreFile, Project, type TaskOptions, javascript, typescript } from "projen";
15
16
  import { ReleaseTrigger } from "projen/lib/release";
17
+ import { mixin, projectPredicate } from "..";
16
18
  import { generateBarrels } from "./barrels.ts";
17
- import { codegenModulePaths, generateCodegen } from "./codegen.ts";
18
- import { DBXToolsConfig, type DBXToolsConfigOptions } from "./dbx-tools-config.ts";
19
- import { resolvePkgRoot } from "./engine-root.ts";
20
- import { PnpmWorkspaceState, type DBXToolsPNPMWorkspaceOptions } from "./pnpm-workspace.ts";
21
- import { applyCompiledPublish } from "./publish.ts";
22
- import { DBXToolsRelease, type StandaloneRelease } from "./release.ts";
23
- import { AGNOSTIC_COMPILER_OPTIONS, PACKAGE_TAG_MIXINS, type PackageTag } from "./tags.ts";
24
- import { DBXToolsRootTsconfig } from "./tsconfig.ts";
25
19
  import {
26
20
  BUN_APP_OVERRIDES,
27
21
  BunBuildFile,
@@ -29,8 +23,9 @@ import {
29
23
  BunfigFile,
30
24
  RootBunfigFile,
31
25
  } from "./bun-app.ts";
32
- import { DBXToolsVsCode } from "./vscode.ts";
33
- import { DEFAULT_RS_PACKAGE_ROOTS, RsPackages } from "./rs-packages.ts";
26
+ import { codegenModulePaths, generateCodegen } from "./codegen.ts";
27
+ import { DBXToolsConfig, type DBXToolsConfigOptions } from "./dbx-tools-config.ts";
28
+ import { resolvePkgRoot } from "./engine-root.ts";
34
29
  import {
35
30
  DEFAULT_PACKAGE_ROOTS,
36
31
  type DiscoveredPackage,
@@ -40,8 +35,12 @@ import {
40
35
  scanPackages,
41
36
  toPosix,
42
37
  } from "./packages.ts";
43
- import { mixin, projectPredicate } from "..";
44
- import { IConstruct } from "constructs";
38
+ import { PnpmWorkspaceState, type DBXToolsPNPMWorkspaceOptions } from "./pnpm-workspace.ts";
39
+ import { applyCompiledPublish } from "./publish.ts";
40
+ import { DBXToolsRelease, type StandaloneRelease } from "./release.ts";
41
+ import { AGNOSTIC_COMPILER_OPTIONS, PACKAGE_TAG_MIXINS, type PackageTag } from "./tags.ts";
42
+ import { DBXToolsRootTsconfig } from "./tsconfig.ts";
43
+ import { DBXToolsVsCode } from "./vscode.ts";
45
44
 
46
45
  /**
47
46
  * The dbx-tools project surface, backed by projen's Node toolchain. A single
@@ -520,11 +519,6 @@ export interface DBXToolsProjectOptions
520
519
  * is a member of the single bun workspace, so the root links it from source.
521
520
  */
522
521
  readonly extraWorkspaceMembers?: readonly string[];
523
- /**
524
- * Experimental TypeScript-to-Node/Python source roots. Defaults to
525
- * `["rs-packages"]`; set `false` to disable the generator.
526
- */
527
- readonly rsPackageRoots?: readonly string[] | false;
528
522
  }
529
523
 
530
524
  /** Options for {@link DBXToolsTypeScriptProject} (a package, or a compiling root). */
@@ -847,7 +841,6 @@ function registerRootTasks(project: javascript.NodeProject): void {
847
841
  applyTasks(project, {
848
842
  barrels: { exec: taskScript(project, "barrels.ts") },
849
843
  openapi: { exec: taskScript(project, "openapi.ts") },
850
- "rs-packages": { exec: taskScript(project, "rs-packages.ts"), receiveArgs: true },
851
844
  clean: { exec: taskScript(project, "clean.ts"), receiveArgs: true },
852
845
  // `receiveArgs` forwards `--watch`, so `bun run sync -- --watch` syncs once
853
846
  // then starts the single node-path watcher loop.
@@ -932,13 +925,6 @@ function initProject(
932
925
  project.vsCode = new DBXToolsVsCode(project);
933
926
 
934
927
  registerRootTasks(project);
935
- project.dbxToolsConfig.rsPackageRoots =
936
- options.rsPackageRoots === false
937
- ? false
938
- : [...(options.rsPackageRoots ?? DEFAULT_RS_PACKAGE_ROOTS)];
939
- if (options.rsPackageRoots !== false) {
940
- new RsPackages(project, options.rsPackageRoots ?? DEFAULT_RS_PACKAGE_ROOTS);
941
- }
942
928
  if (options.prettier || project.prettier) {
943
929
  const formatTask = project.tasks.tryFind("format") ?? project.addTask("format");
944
930
  formatTask.prependExec("prettier . --write", { receiveArgs: true });
@@ -967,14 +953,20 @@ function initProject(
967
953
  // each file to its own package tsconfig (so type-aware rules work tree-wide), and
968
954
  // `import/no-extraneous-dependencies` still checks each file against its nearest
969
955
  // package.json. Formatting defers to the root Prettier to avoid rule/formatter
970
- // conflicts (e.g. quote style). Spawned from `test`, so ignorePatterns must cover
971
- // every read-only generated path - `--fix` EACCES-crashes on them otherwise.
956
+ // conflicts (e.g. quote style). The normal task is check-only so CI never
957
+ // repairs the worktree it is meant to validate; `eslint:fix` is the explicit
958
+ // local mutation path.
972
959
  const eslint = new javascript.Eslint(project, {
973
- dirs: [...roots],
960
+ dirs: [...roots, "projen"],
974
961
  fileExtensions: [".ts", ".tsx"],
975
962
  projectService: true,
976
963
  prettier: Boolean(project.prettier),
977
964
  tsconfigPath: "./tsconfig.json",
965
+ commandOptions: { fix: false },
966
+ });
967
+ project.addTask("eslint:fix", {
968
+ description: "Fix ESLint issues across the codebase",
969
+ exec: "bun run eslint -- --fix",
978
970
  });
979
971
  // Generated read-only outputs (barrels, openapi clients, app scripts, codegen).
980
972
  // ESLint --fix cannot rewrite them; they are stamped by the barrel generator /
@@ -983,6 +975,7 @@ function initProject(
983
975
  eslint.addIgnorePattern(`${root}/openapi/**`);
984
976
  eslint.addIgnorePattern(`${root}/**/index.ts`);
985
977
  }
978
+ eslint.addIgnorePattern("projen/index.ts");
986
979
  // The generated bun app scripts + unmanaged overrides live at the package root,
987
980
  // outside any `src/**` tsconfig include, so the type-aware parser cannot resolve
988
981
  // them to a project. ESLint still cannot parse them.
@@ -1256,21 +1249,24 @@ export function applyToProjects<P extends Project>(
1256
1249
  let pred = projectPredicate.isProject();
1257
1250
  if (!options?.includeNonDBXToolsProjects) pred = pred.and(projectPredicate.isDBXToolsProject());
1258
1251
  if (!options?.includeRoots) pred = pred.and((p) => p.parent != null);
1259
- if (options?.identifierPackageName)
1252
+ if (options?.identifierPackageName) {
1260
1253
  pred = pred.and(
1261
1254
  projectPredicate.hasIdentifierPackageName(
1262
1255
  ...object.toOneOrMany(options.identifierPackageName),
1263
1256
  ),
1264
1257
  );
1258
+ }
1265
1259
  if (options?.name) pred = pred.and(projectPredicate.hasName(...object.toOneOrMany(options.name)));
1266
- if (options?.identifierScope)
1260
+ if (options?.identifierScope) {
1267
1261
  pred = pred.and(
1268
1262
  projectPredicate.hasIdentifierScope(...object.toOneOrMany(options.identifierScope)),
1269
1263
  );
1270
- if (options?.identifierName)
1264
+ }
1265
+ if (options?.identifierName) {
1271
1266
  pred = pred.and(
1272
1267
  projectPredicate.hasIdentifierName(...object.toOneOrMany(options.identifierName)),
1273
1268
  );
1269
+ }
1274
1270
  if (options?.tags) pred = pred.and(projectPredicate.hasTag(...object.toOneOrMany(options.tags)));
1275
1271
  if (options?.path) pred = pred.and(projectPredicate.hasPath(...object.toOneOrMany(options.path)));
1276
1272
  const projectMixin = mixin.create(pred, (p) => {
package/src/publish.ts CHANGED
@@ -29,8 +29,8 @@
29
29
  */
30
30
  import type { javascript } from "projen";
31
31
  import { typescript } from "projen";
32
- import { addPackageFiles, applyCompilerOptions, applyIncludes } from "./project.ts";
33
32
  import { isDBXToolsProject } from "./project-predicate.ts";
33
+ import { addPackageFiles, applyCompilerOptions, applyIncludes } from "./project.ts";
34
34
 
35
35
  /** Directory `tsc` emits into, and the root of every published entry point. */
36
36
  export const COMPILED_DIR = "lib";
package/src/tags.ts CHANGED
@@ -17,6 +17,7 @@ import type { IMixin as ConstructsMixin } from "constructs";
17
17
  import { javascript } from "projen";
18
18
  import { BunBuildFile, BunDevServerFile, BunfigFile } from "./bun-app.ts";
19
19
  import { create } from "./mixin.ts";
20
+ import * as projectPredicate from "./project-predicate.ts";
20
21
  import {
21
22
  addPackageFiles,
22
23
  applyCompilerOptions,
@@ -25,7 +26,6 @@ import {
25
26
  applyTasks,
26
27
  srcModuleExports,
27
28
  } from "./project.ts";
28
- import * as projectPredicate from "./project-predicate.ts";
29
29
 
30
30
  /** Node compiler options: ES2022 lib + node types, deliberately no DOM. */
31
31
  const NODE_COMPILER_OPTIONS: javascript.TypeScriptCompilerOptions = {
@@ -137,7 +137,7 @@ export const PACKAGE_TAG_MIXINS = {
137
137
  // register and no launcher shim to generate. In a WORKSPACE checkout the
138
138
  // manifest still points at the `.ts` entry, which Node type-strips on its own
139
139
  // because the package is not under `node_modules`.
140
- p.addDeps("commander@catalog:", "@clack/prompts@catalog:");
140
+ p.addDeps("commander@catalog:");
141
141
  p.addDevDeps("@types/node@catalog:");
142
142
  // A CLI compiles code OUTSIDE `src/` - its root `index.ts` barrel and the
143
143
  // `bin/` entries - which the src-only tag default doesn't reach.
package/tasks/barrels.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env -S bun
2
2
  import { sep } from "node:path";
3
- import { generateBarrels } from "../src/barrels.ts";
4
3
  import { log, string } from "@dbx-tools/shared-core";
5
- import { watchLoop, watchRoots } from "../src/watch.ts";
4
+ import { generateBarrels } from "../src/barrels.ts";
6
5
  import { recordedPackages } from "../src/packages.ts";
6
+ import { watchLoop, watchRoots } from "../src/watch.ts";
7
7
 
8
8
  const logger = log.logger("projen:barrels");
9
9
 
package/tasks/bump.ts CHANGED
@@ -39,12 +39,12 @@
39
39
  * - `false`: never publish locally.
40
40
  * - a URL: always publish to that registry.
41
41
  */
42
- import { exec, project } from "@dbx-tools/core";
43
- import { log, net } from "@dbx-tools/shared-core";
44
- import { Command, Option } from "commander";
45
42
  import { chmodSync, existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
46
43
  import { resolve } from "node:path";
47
44
  import { fileURLToPath } from "node:url";
45
+ import { exec, project } from "@dbx-tools/core";
46
+ import { log, net } from "@dbx-tools/shared-core";
47
+ import { Command, Option } from "commander";
48
48
 
49
49
  const logger = log.logger("projen:bump");
50
50
  const LEVELS = ["patch", "minor", "major"] as const;
package/tasks/clean.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env -S bun
2
2
  import { relative } from "node:path";
3
- import { listGeneratedFiles, listNodeModulesDirs, removePaths } from "../src/clean.ts";
4
3
  import { log, string } from "@dbx-tools/shared-core";
4
+ import { listGeneratedFiles, listNodeModulesDirs, removePaths } from "../src/clean.ts";
5
5
  import { repoRoot, toPosix } from "../src/packages.ts";
6
6
 
7
7
  const logger = log.logger("projen:clean");
package/tasks/openapi.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env -S bun
2
- import { generateBarrels } from "../src/barrels.ts";
3
2
  import { log, string } from "@dbx-tools/shared-core";
3
+ import { generateBarrels } from "../src/barrels.ts";
4
4
  import { generateOpenapi, isTsoaController } from "../src/openapi.ts";
5
5
  import { runSynth } from "../src/scaffold.ts";
6
6
  import { watchLoop, watchRoots } from "../src/watch.ts";
package/tasks/projenrc.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env -S bun
2
2
  import { resolve } from "node:path";
3
3
  import { log } from "@dbx-tools/shared-core";
4
+ import { repoRoot, syncResynthPaths } from "../src/packages.ts";
4
5
  import { runSynth } from "../src/scaffold.ts";
5
6
  import { watchLoop } from "../src/watch.ts";
6
- import { repoRoot, syncResynthPaths } from "../src/packages.ts";
7
7
 
8
8
  const logger = log.logger("projen:projenrc");
9
9
 
package/tasks/sync.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env -S bun
2
2
  import { fileURLToPath } from "node:url";
3
- import concurrently from "concurrently";
4
3
  import { log } from "@dbx-tools/shared-core";
4
+ import concurrently from "concurrently";
5
5
  import { runSynth } from "../src/scaffold.ts";
6
6
 
7
7
  const logger = log.logger("projen:sync");
@@ -57,11 +57,6 @@ if (!process.argv.includes("--watch")) {
57
57
  { command: `bun "${taskPath("projenrc.ts")}"`, name: "projenrc", prefixColor: "magenta" },
58
58
  { command: `bun "${taskPath("barrels.ts")}" --watch`, name: "barrels", prefixColor: "cyan" },
59
59
  { command: `bun "${taskPath("openapi.ts")}" --watch`, name: "openapi", prefixColor: "green" },
60
- {
61
- command: `bun "${taskPath("rs-packages.ts")}" --watch`,
62
- name: "rs-packages",
63
- prefixColor: "yellow",
64
- },
65
60
  ],
66
61
  {
67
62
  prefix: "name",
@@ -1,237 +0,0 @@
1
- /**
2
- * Experimental Rosetta-source generation.
3
- *
4
- * A TypeScript file under `rs-packages/<package>/src/` is the canonical Node
5
- * implementation. Projen copies its TypeScript surface to the matching
6
- * `packages/<package>/src/` path and extracts one or more `@rs-python` blocks
7
- * into a conventional Python package path.
8
- */
9
- import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
10
- import { dirname, extname, join, relative, resolve } from "node:path";
11
- import { json, object } from "@dbx-tools/shared-core";
12
- import { Component, javascript } from "projen";
13
- import { header, makeReadonly, makeWritable } from "./generated.ts";
14
- import { toPosix } from "./packages.ts";
15
-
16
- export const DEFAULT_RS_PACKAGE_ROOTS = ["rs-packages"] as const;
17
-
18
- const SOURCE_EXTENSIONS = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
19
- const PYTHON_BLOCK = /\/\*\s*@rs-python\s*\n([\s\S]*?)\n\s*@rs-end\s*\*\//g;
20
- const NODE_OVERRIDE = /^\s*\/\/\s*@rs-node\s+(.+?)\s*$/m;
21
- const PYTHON_OVERRIDE = /^\s*\/\/\s*@rs-python-path\s+(.+?)\s*$/m;
22
- const RS_METADATA = /^\s*\/\/\s*@rs-(?:node|python-path)\s+.+?\s*$\n?/gm;
23
- const NODE_MARKER = "// GENERATED by projen synth (rs-packages)";
24
- const PYTHON_MARKER = "# GENERATED by projen synth (rs-packages)";
25
-
26
- export interface RsPackageOutput {
27
- readonly source: string;
28
- readonly node: string;
29
- readonly python: string;
30
- }
31
-
32
- export interface GenerateRsPackagesOptions {
33
- readonly projectRoot?: string;
34
- readonly roots?: readonly string[];
35
- }
36
-
37
- function sourceFiles(root: string): string[] {
38
- if (!existsSync(root)) return [];
39
- const files: string[] = [];
40
- const visit = (dir: string): void => {
41
- for (const entry of readdirSync(dir)) {
42
- const path = join(dir, entry);
43
- if (statSync(path).isDirectory()) visit(path);
44
- else if (SOURCE_EXTENSIONS.has(extname(entry))) files.push(path);
45
- }
46
- };
47
- visit(root);
48
- return files.sort();
49
- }
50
-
51
- function dedent(value: string): string {
52
- const lines = value.replace(/^\n+|\n+$/g, "").split("\n");
53
- const indents = lines
54
- .filter((line) => line.trim())
55
- .map((line) => line.match(/^\s*/)?.[0].length ?? 0);
56
- const width = indents.length ? Math.min(...indents) : 0;
57
- return lines.map((line) => line.slice(width)).join("\n");
58
- }
59
-
60
- function defaultOutputPaths(
61
- projectRoot: string,
62
- root: string,
63
- source: string,
64
- ): Pick<RsPackageOutput, "node" | "python"> {
65
- const sourceRelative = toPosix(relative(resolve(projectRoot, root), source));
66
- const segments = sourceRelative.split("/");
67
- const srcIndex = segments.indexOf("src");
68
- if (srcIndex < 1) {
69
- throw new Error(
70
- `Rosetta source must live under <package>/src/: ${toPosix(relative(projectRoot, source))}`,
71
- );
72
- }
73
-
74
- const packageSegments = segments.slice(0, srcIndex);
75
- const moduleSegments = segments.slice(srcIndex + 1);
76
- const moduleFile = moduleSegments.pop();
77
- if (!moduleFile) throw new Error(`Rosetta source has no module name: ${source}`);
78
-
79
- const pythonDistribution = packageSegments.join("-");
80
- const pythonImport = packageSegments.join("_").replace(/-/g, "_");
81
- const pythonModule = `${moduleFile.slice(0, -extname(moduleFile).length).replace(/-/g, "_")}.py`;
82
-
83
- return {
84
- node: resolve(
85
- projectRoot,
86
- "packages",
87
- ...packageSegments,
88
- "src",
89
- ...moduleSegments,
90
- moduleFile,
91
- ),
92
- python: resolve(
93
- projectRoot,
94
- "python-packages",
95
- pythonDistribution,
96
- "src",
97
- pythonImport,
98
- ...moduleSegments,
99
- pythonModule,
100
- ),
101
- };
102
- }
103
-
104
- function resolveOutput(
105
- projectRoot: string,
106
- override: RegExpMatchArray | null,
107
- fallback: string,
108
- ): string {
109
- const value = override?.[1]?.trim();
110
- return value ? resolve(projectRoot, value) : fallback;
111
- }
112
-
113
- function assertWritableOutput(projectRoot: string, path: string, marker: string): void {
114
- if (!existsSync(path)) return;
115
- if (readFileSync(path, "utf8").startsWith(marker)) return;
116
- throw new Error(
117
- `Refusing to overwrite hand-authored Rosetta output: ${toPosix(relative(projectRoot, path))}`,
118
- );
119
- }
120
-
121
- function renderSource(projectRoot: string, root: string, source: string): RsPackageOutput {
122
- const input = readFileSync(source, "utf8");
123
- const defaults = defaultOutputPaths(projectRoot, root, source);
124
- const node = resolveOutput(projectRoot, input.match(NODE_OVERRIDE), defaults.node);
125
- const python = resolveOutput(projectRoot, input.match(PYTHON_OVERRIDE), defaults.python);
126
- const pythonBlocks = [...input.matchAll(PYTHON_BLOCK)].map((match) => dedent(match[1] ?? ""));
127
-
128
- if (!pythonBlocks.length) {
129
- throw new Error(
130
- `Rosetta source needs at least one /* @rs-python ... @rs-end */ block: ${toPosix(relative(projectRoot, source))}`,
131
- );
132
- }
133
-
134
- const nodeBody = input.replace(PYTHON_BLOCK, "").replace(RS_METADATA, "").trim();
135
- const nodeContent = `${header({
136
- tool: "projen synth (rs-packages)",
137
- source: toPosix(relative(projectRoot, source)),
138
- })}\n${nodeBody}\n`;
139
- const pythonContent = [
140
- "# GENERATED by projen synth (rs-packages) - DO NOT EDIT.",
141
- `# Regenerated from ${toPosix(relative(projectRoot, source))}.`,
142
- "# Hand edits are overwritten; edit the Rosetta source instead.",
143
- "",
144
- pythonBlocks.join("\n\n"),
145
- "",
146
- ].join("\n");
147
-
148
- for (const [path, content] of [
149
- [node, nodeContent],
150
- [python, pythonContent],
151
- ] as const) {
152
- assertWritableOutput(projectRoot, path, path === node ? NODE_MARKER : PYTHON_MARKER);
153
- mkdirSync(dirname(path), { recursive: true });
154
- makeWritable(path);
155
- writeFileSync(path, content);
156
- makeReadonly(path);
157
- }
158
-
159
- return { source, node, python };
160
- }
161
-
162
- /** Read the roots recorded by synth for standalone tasks and watchers. */
163
- export function configuredRsPackageRoots(projectRoot: string): readonly string[] | false {
164
- const manifest = json.parseRecord(readFileSync(resolve(projectRoot, "package.json"), "utf8"));
165
- const config = object.isRecord(manifest?.dbxToolsConfig) ? manifest.dbxToolsConfig : undefined;
166
- const configured = config?.rsPackageRoots;
167
- if (configured === false) return false;
168
- if (Array.isArray(configured)) {
169
- const roots = configured.filter(
170
- (value): value is string => typeof value === "string" && !!value,
171
- );
172
- if (roots.length) return roots;
173
- }
174
- return DEFAULT_RS_PACKAGE_ROOTS;
175
- }
176
-
177
- export function discoverRsPackageOutputs(
178
- options: GenerateRsPackagesOptions = {},
179
- ): RsPackageOutput[] {
180
- const projectRoot = resolve(options.projectRoot ?? process.cwd());
181
- const roots = options.roots ?? DEFAULT_RS_PACKAGE_ROOTS;
182
- return roots.flatMap((root) =>
183
- sourceFiles(resolve(projectRoot, root)).map((source) => {
184
- const defaults = defaultOutputPaths(projectRoot, root, source);
185
- const input = readFileSync(source, "utf8");
186
- return {
187
- source,
188
- node: resolveOutput(projectRoot, input.match(NODE_OVERRIDE), defaults.node),
189
- python: resolveOutput(projectRoot, input.match(PYTHON_OVERRIDE), defaults.python),
190
- };
191
- }),
192
- );
193
- }
194
-
195
- export function generateRsPackages(options: GenerateRsPackagesOptions = {}): RsPackageOutput[] {
196
- const projectRoot = resolve(options.projectRoot ?? process.cwd());
197
- const roots = options.roots ?? DEFAULT_RS_PACKAGE_ROOTS;
198
- return roots.flatMap((root) =>
199
- sourceFiles(resolve(projectRoot, root)).map((source) =>
200
- renderSource(projectRoot, root, source),
201
- ),
202
- );
203
- }
204
-
205
- /** Projen lifecycle component that regenerates Rosetta outputs after synth. */
206
- export class RsPackages extends Component {
207
- private readonly outputs: RsPackageOutput[];
208
-
209
- constructor(
210
- project: javascript.NodeProject,
211
- private readonly roots: readonly string[] = DEFAULT_RS_PACKAGE_ROOTS,
212
- ) {
213
- super(project);
214
- // Generate during construction so a brand-new Node package exists before the
215
- // root's package scan later in the same synth. postSynthesize repeats this to
216
- // restore outputs after projen has written the rest of the tree.
217
- this.outputs = generateRsPackages({ projectRoot: project.outdir, roots });
218
- for (const output of this.outputs) {
219
- const node = `/${toPosix(relative(project.outdir, output.node))}`;
220
- const python = `/${toPosix(relative(project.outdir, output.python))}`;
221
- project.annotateGenerated(node);
222
- project.annotateGenerated(python);
223
- }
224
- }
225
-
226
- public override preSynthesize(): void {
227
- const eslint = javascript.Eslint.of(this.project);
228
- if (!eslint) return;
229
- for (const output of this.outputs) {
230
- eslint.addIgnorePattern(toPosix(relative(this.project.outdir, output.node)));
231
- }
232
- }
233
-
234
- public override postSynthesize(): void {
235
- generateRsPackages({ projectRoot: this.project.outdir, roots: this.roots });
236
- }
237
- }
@@ -1,26 +0,0 @@
1
- import { existsSync } from "node:fs";
2
- import { resolve } from "node:path";
3
- import { log } from "@dbx-tools/shared-core";
4
- import { configuredRsPackageRoots, generateRsPackages } from "../src/rs-packages.ts";
5
- import { repoRoot } from "../src/packages.ts";
6
- import { watchLoop } from "../src/watch.ts";
7
-
8
- const logger = log.logger("projen:rs-packages");
9
- const configuredRoots = configuredRsPackageRoots(repoRoot);
10
- const roots = configuredRoots === false ? [] : configuredRoots;
11
- const watchRoots = roots.map((root) => resolve(repoRoot, root)).filter(existsSync);
12
-
13
- function generate(): void {
14
- const outputs = generateRsPackages({ projectRoot: repoRoot, roots });
15
- logger.success(`generated ${outputs.length} Rosetta module${outputs.length === 1 ? "" : "s"}`);
16
- }
17
-
18
- generate();
19
-
20
- if (process.argv.includes("--watch")) {
21
- if (watchRoots.length) watchLoop("rs-packages", watchRoots, generate);
22
- else {
23
- logger.info("no Rosetta roots configured; watcher is idle");
24
- setInterval(() => {}, 2_147_483_647);
25
- }
26
- }