@dbx-tools/projen 0.6.57 → 0.6.59
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 +42 -10
- package/index.ts +4 -1
- package/package.json +4 -4
- package/src/project.ts +14 -0
- package/src/rs-packages.ts +237 -0
- package/tasks/rs-packages.ts +26 -0
- package/tasks/sync.ts +6 -1
package/README.md
CHANGED
|
@@ -124,6 +124,35 @@ 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
|
+
|
|
127
156
|
## Generate OpenAPI Clients
|
|
128
157
|
|
|
129
158
|
```ts
|
|
@@ -188,6 +217,7 @@ file contract as the CLI.
|
|
|
188
217
|
- `pnpmWorkspace` - generated pnpm workspace file and catalog model.
|
|
189
218
|
- `barrels` / `moduleExports` - public entrypoint generation.
|
|
190
219
|
- `codegen` - `.d.ts` to zod schema generation.
|
|
220
|
+
- `rsPackages` - annotated TypeScript source to Node/Python generation.
|
|
191
221
|
- `openapi` - tsoa/OpenAPI package generation.
|
|
192
222
|
- `bunApp` / `tsconfig` / `vscode` - generated support files/components.
|
|
193
223
|
- `generated` / `clean` / `watch` / `scaffold` - read-only file stamping,
|
|
@@ -196,8 +226,9 @@ file contract as the CLI.
|
|
|
196
226
|
- `engineRoot` - engine package root resolution for bootstrapped repos.
|
|
197
227
|
|
|
198
228
|
The engine registers its commands as projen tasks on the workspace root, so run
|
|
199
|
-
them with `bun run <task>` - `sync` (add `--watch`), `barrels`, `openapi`,
|
|
200
|
-
`
|
|
229
|
+
them with `bun run <task>` - `sync` (add `--watch`), `barrels`, `openapi`,
|
|
230
|
+
`rs-packages` (also supports `--watch`), and `clean`.
|
|
231
|
+
[`@dbx-tools/cli`](../packages/cli/dbx-tools) is only needed to
|
|
201
232
|
bootstrap a folder that has no `.projenrc.ts` or toolchain yet.
|
|
202
233
|
|
|
203
234
|
## Run Tasks From The ROOT
|
|
@@ -206,14 +237,15 @@ Every repo-wide task lives on the root, and the root's `compile` / `test`
|
|
|
206
237
|
delegate with `bun run --filter '*'` rather than emitting a step per member - so
|
|
207
238
|
a new package is covered without a re-synth. Work from the root:
|
|
208
239
|
|
|
209
|
-
| Task
|
|
210
|
-
|
|
|
211
|
-
| `bun run build`
|
|
212
|
-
| `bun run compile`
|
|
213
|
-
| `bun run test`
|
|
214
|
-
| `bun run sync`
|
|
215
|
-
| `bun run barrels`
|
|
216
|
-
| `bun run
|
|
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 |
|
|
217
249
|
|
|
218
250
|
Members intentionally keep only the tasks that something OTHER than a human
|
|
219
251
|
invokes, so there is no second place to run the same thing:
|
package/index.ts
CHANGED
|
@@ -18,11 +18,13 @@ 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";
|
|
21
22
|
export * as scaffold from "./src/scaffold.ts";
|
|
22
23
|
export * as tags from "./src/tags.ts";
|
|
23
24
|
export * as tsconfig from "./src/tsconfig.ts";
|
|
24
25
|
export * as vscode from "./src/vscode.ts";
|
|
25
26
|
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
28
|
export { DBXToolsConfig } from "./src/dbx-tools-config.ts";
|
|
27
29
|
export type { DBXToolsConfigOptions } from "./src/dbx-tools-config.ts";
|
|
28
30
|
export { resolvePkgRoot } from "./src/engine-root.ts";
|
|
@@ -38,9 +40,10 @@ export type { DBXToolsProject, DBXToolsProjectOptions, DBXToolsTypeScriptProject
|
|
|
38
40
|
export { COMPILED_DIR, COMPILED_COMPILER_OPTIONS } from "./src/publish.ts";
|
|
39
41
|
export { DBXToolsRelease } from "./src/release.ts";
|
|
40
42
|
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";
|
|
41
45
|
export { AGNOSTIC_COMPILER_OPTIONS, PACKAGE_TAG_MIXINS } from "./src/tags.ts";
|
|
42
46
|
export type { PackageTag } from "./src/tags.ts";
|
|
43
47
|
export { DBXToolsRootTsconfig } from "./src/tsconfig.ts";
|
|
44
|
-
export { BUN_DEV_OVERRIDE, BUN_BUILD_OVERRIDE, BUN_APP_OVERRIDES, BunfigFile, BunDevServerFile, BunBuildFile } from "./src/bun-app.ts";
|
|
45
48
|
export { DBXToolsVsCode } from "./src/vscode.ts";
|
|
46
49
|
export type { IgnoreGroupOptions } from "./src/watch.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.
|
|
30
|
-
"@dbx-tools/path": "0.6.
|
|
31
|
-
"@dbx-tools/shared-core": "0.6.
|
|
29
|
+
"@dbx-tools/core": "0.6.59",
|
|
30
|
+
"@dbx-tools/path": "0.6.59",
|
|
31
|
+
"@dbx-tools/shared-core": "0.6.59",
|
|
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.
|
|
50
|
+
"version": "0.6.59",
|
|
51
51
|
"types": "index.ts",
|
|
52
52
|
"type": "module",
|
|
53
53
|
"exports": {
|
package/src/project.ts
CHANGED
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
RootBunfigFile,
|
|
31
31
|
} from "./bun-app.ts";
|
|
32
32
|
import { DBXToolsVsCode } from "./vscode.ts";
|
|
33
|
+
import { DEFAULT_RS_PACKAGE_ROOTS, RsPackages } from "./rs-packages.ts";
|
|
33
34
|
import {
|
|
34
35
|
DEFAULT_PACKAGE_ROOTS,
|
|
35
36
|
type DiscoveredPackage,
|
|
@@ -519,6 +520,11 @@ export interface DBXToolsProjectOptions
|
|
|
519
520
|
* is a member of the single bun workspace, so the root links it from source.
|
|
520
521
|
*/
|
|
521
522
|
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;
|
|
522
528
|
}
|
|
523
529
|
|
|
524
530
|
/** Options for {@link DBXToolsTypeScriptProject} (a package, or a compiling root). */
|
|
@@ -841,6 +847,7 @@ function registerRootTasks(project: javascript.NodeProject): void {
|
|
|
841
847
|
applyTasks(project, {
|
|
842
848
|
barrels: { exec: taskScript(project, "barrels.ts") },
|
|
843
849
|
openapi: { exec: taskScript(project, "openapi.ts") },
|
|
850
|
+
"rs-packages": { exec: taskScript(project, "rs-packages.ts"), receiveArgs: true },
|
|
844
851
|
clean: { exec: taskScript(project, "clean.ts"), receiveArgs: true },
|
|
845
852
|
// `receiveArgs` forwards `--watch`, so `bun run sync -- --watch` syncs once
|
|
846
853
|
// then starts the single node-path watcher loop.
|
|
@@ -925,6 +932,13 @@ function initProject(
|
|
|
925
932
|
project.vsCode = new DBXToolsVsCode(project);
|
|
926
933
|
|
|
927
934
|
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
|
+
}
|
|
928
942
|
if (options.prettier || project.prettier) {
|
|
929
943
|
const formatTask = project.tasks.tryFind("format") ?? project.addTask("format");
|
|
930
944
|
formatTask.prependExec("prettier . --write", { receiveArgs: true });
|
|
@@ -0,0 +1,237 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
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
|
+
}
|
package/tasks/sync.ts
CHANGED
|
@@ -31,7 +31,7 @@ if (!process.argv.includes("--watch")) {
|
|
|
31
31
|
runSynth({ post: true });
|
|
32
32
|
logger.success("synced");
|
|
33
33
|
} else {
|
|
34
|
-
// Watch: one initial full synth to bring the tree up to date, then
|
|
34
|
+
// Watch: one initial full synth to bring the tree up to date, then focused
|
|
35
35
|
// watchers under `concurrently`. The projenrc watcher is the intelligent stand-in
|
|
36
36
|
// for stock `projen --watch` - it re-synths (+install) ONLY when `.projenrc.ts` or
|
|
37
37
|
// a configured `syncResynthPaths` entry changes, while barrels/openapi keep generated
|
|
@@ -57,6 +57,11 @@ 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
|
+
},
|
|
60
65
|
],
|
|
61
66
|
{
|
|
62
67
|
prefix: "name",
|