@dbx-tools/projen 0.9.21 → 0.9.28
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 +77 -207
- package/index.ts +10 -12
- package/package.json +4 -4
- package/src/barrels.ts +1 -74
- package/src/module-exports.ts +175 -1
- package/src/openapi.ts +5 -72
- package/src/packages.ts +45 -0
- package/src/project-js.ts +3 -6
- package/src/project-predicate.ts +1 -2
- package/src/project-py.ts +82 -103
- package/src/project.ts +2 -2
- package/src/python-node-bindings.ts +138 -0
- package/src/python-node-bundle.ts +92 -0
- package/src/release-catalog.ts +3 -5
- package/src/{release-dispatch.ts → release-context.ts} +0 -11
- package/src/release.ts +70 -152
- package/tasks/databricks-cli-assets.ts +129 -0
- package/tasks/local-publish.ts +23 -107
- package/tasks/publish-npm.ts +10 -1
- package/tasks/publish-python.ts +26 -11
- package/tasks/publish.ts +12 -48
- package/tasks/python-node-bindings-watch.ts +47 -0
- package/tasks/python-node-bindings.ts +1017 -0
- package/tasks/python-release.ts +74 -0
- package/tasks/release.ts +99 -0
- package/tasks/sync.ts +13 -18
- package/tasks/version-check.ts +1 -28
- package/._LICENSE +0 -0
- package/._README.md +0 -0
- package/._index.ts +0 -0
- package/._package.json +0 -0
- package/src/.__lazy-require.ts +0 -0
- package/src/.__release-platform.ts +0 -0
- package/src/.__rust-project.ts +0 -0
- package/src/.__rust-release-workflow.ts +0 -0
- package/src/.__task-command.ts +0 -0
- package/src/._barrels.ts +0 -0
- package/src/._bun-app.ts +0 -0
- package/src/._bun-workflow.ts +0 -0
- package/src/._clean.ts +0 -0
- package/src/._codegen.ts +0 -0
- package/src/._dbx-tools-config.ts +0 -0
- package/src/._engine-root.ts +0 -0
- package/src/._generated.ts +0 -0
- package/src/._mixin.ts +0 -0
- package/src/._module-exports.ts +0 -0
- package/src/._openapi.ts +0 -0
- package/src/._packages.ts +0 -0
- package/src/._pnpm-workspace.ts +0 -0
- package/src/._project-js.ts +0 -0
- package/src/._project-predicate.ts +0 -0
- package/src/._project-py.ts +0 -0
- package/src/._project-rs.ts +0 -0
- package/src/._project.ts +0 -0
- package/src/._projen-version.ts +0 -0
- package/src/._publish.ts +0 -0
- package/src/._release-catalog.ts +0 -0
- package/src/._release-dispatch.ts +0 -0
- package/src/._release-github.ts +0 -0
- package/src/._release.ts +0 -0
- package/src/._scaffold.ts +0 -0
- package/src/._tags.ts +0 -0
- package/src/._tsconfig.ts +0 -0
- package/src/._uniffi.ts +0 -0
- package/src/._vscode.ts +0 -0
- package/src/._watch.ts +0 -0
- package/src/._workspace-lock.ts +0 -0
- package/src/._workspace-version.ts +0 -0
- package/src/_rust-project.ts +0 -429
- package/src/_rust-release-workflow.ts +0 -311
- package/src/project-rs.ts +0 -1054
- package/src/uniffi.ts +0 -65
- package/tasks/._barrels.ts +0 -0
- package/tasks/._bump.ts +0 -0
- package/tasks/._clean.ts +0 -0
- package/tasks/._compile-workspace.ts +0 -0
- package/tasks/._local-publish.ts +0 -0
- package/tasks/._openapi.ts +0 -0
- package/tasks/._projenrc.ts +0 -0
- package/tasks/._publish-npm.ts +0 -0
- package/tasks/._publish-python.ts +0 -0
- package/tasks/._publish-uniffi-local.ts +0 -0
- package/tasks/._publish.ts +0 -0
- package/tasks/._python-registry.ts +0 -0
- package/tasks/._release-assets.ts +0 -0
- package/tasks/._release-candidate.ts +0 -0
- package/tasks/._release-manifest.ts +0 -0
- package/tasks/._release-pr.ts +0 -0
- package/tasks/._release-summary.ts +0 -0
- package/tasks/._release-version.ts +0 -0
- package/tasks/._rust.ts +0 -0
- package/tasks/._sync.ts +0 -0
- package/tasks/._uniffi-python.d.ts +0 -0
- package/tasks/._uniffi-python.js +0 -0
- package/tasks/._uniffi-release.mjs +0 -0
- package/tasks/._uniffi.ts +0 -0
- package/tasks/._version-check.ts +0 -0
- package/tasks/publish-uniffi-local.ts +0 -359
- package/tasks/release-assets.ts +0 -467
- package/tasks/release-candidate.ts +0 -416
- package/tasks/release-manifest.ts +0 -273
- package/tasks/release-pr.ts +0 -589
- package/tasks/release-summary.ts +0 -379
- package/tasks/rust.ts +0 -173
- package/tasks/uniffi-python.d.ts +0 -60
- package/tasks/uniffi-python.js +0 -178
- package/tasks/uniffi-release.mjs +0 -579
- package/tasks/uniffi.ts +0 -262
package/src/module-exports.ts
CHANGED
|
@@ -19,8 +19,9 @@
|
|
|
19
19
|
* `export { ... }` is a hard error (TS1205). `export function` names are marked
|
|
20
20
|
* `isFunction` so the barrel leaves them namespace-only.
|
|
21
21
|
*/
|
|
22
|
-
import { readFileSync } from "node:fs";
|
|
22
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
23
23
|
import { createRequire } from "node:module";
|
|
24
|
+
import { dirname, extname, resolve } from "node:path";
|
|
24
25
|
import type { parseSync as OxcParseSync } from "oxc-parser";
|
|
25
26
|
|
|
26
27
|
const require = createRequire(import.meta.url);
|
|
@@ -63,6 +64,13 @@ export interface ModuleExport {
|
|
|
63
64
|
readonly isFunction: boolean;
|
|
64
65
|
}
|
|
65
66
|
|
|
67
|
+
/** Plain public function reachable through a module's re-export graph. */
|
|
68
|
+
export interface PublicFunctionExport {
|
|
69
|
+
readonly name: string;
|
|
70
|
+
readonly sourceFile: string;
|
|
71
|
+
readonly sourceName: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
66
74
|
/** Declaration node types that are inherently type-only. */
|
|
67
75
|
const TYPE_DECLARATIONS = new Set(["TSInterfaceDeclaration", "TSTypeAliasDeclaration"]);
|
|
68
76
|
|
|
@@ -132,3 +140,169 @@ export function moduleExports(file: string): ModuleExport[] {
|
|
|
132
140
|
}
|
|
133
141
|
return [...byName.values()];
|
|
134
142
|
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Resolve every plain named function exported by `file`, following relative
|
|
146
|
+
* named and star re-exports. Non-function values and types are ignored. A
|
|
147
|
+
* generator, declaration-only overload, or conflicting function definition is
|
|
148
|
+
* rejected because a generic runtime bridge cannot preserve those semantics.
|
|
149
|
+
*/
|
|
150
|
+
export function publicFunctionExports(file: string): PublicFunctionExport[] {
|
|
151
|
+
return [...collectPublicFunctions(resolve(file), new Set()).values()]
|
|
152
|
+
.map(({ name, sourceFile, sourceName }) => ({ name, sourceFile, sourceName }))
|
|
153
|
+
.sort((left, right) => left.name.localeCompare(right.name));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
interface ResolvedFunctionExport extends PublicFunctionExport {
|
|
157
|
+
readonly source: string;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function collectPublicFunctions(
|
|
161
|
+
file: string,
|
|
162
|
+
visiting: Set<string>,
|
|
163
|
+
): Map<string, ResolvedFunctionExport> {
|
|
164
|
+
if (visiting.has(file)) return new Map();
|
|
165
|
+
visiting.add(file);
|
|
166
|
+
const source = readFileSync(file, "utf8");
|
|
167
|
+
const body = parseSync(file, source).program.body;
|
|
168
|
+
const functions = new Map<string, ResolvedFunctionExport>();
|
|
169
|
+
const localFunctions = new Map<string, FunctionNode>();
|
|
170
|
+
|
|
171
|
+
for (const statement of body) {
|
|
172
|
+
const node = statement as FunctionNode & { declaration?: FunctionNode | null };
|
|
173
|
+
const declaration =
|
|
174
|
+
node.type === "FunctionDeclaration" || node.type === "TSDeclareFunction"
|
|
175
|
+
? node
|
|
176
|
+
: node.declaration;
|
|
177
|
+
if (declaration?.type === "FunctionDeclaration" || declaration?.type === "TSDeclareFunction") {
|
|
178
|
+
const name = declaration.id?.name;
|
|
179
|
+
if (name) localFunctions.set(name, declaration);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const add = (exported: ResolvedFunctionExport): void => {
|
|
184
|
+
const existing = functions.get(exported.name);
|
|
185
|
+
if (existing && existing.source !== exported.source) {
|
|
186
|
+
throw new Error(
|
|
187
|
+
`Conflicting function export ${exported.name} from ${existing.source} and ${exported.source}`,
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
functions.set(exported.name, exported);
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
for (const statement of body) {
|
|
194
|
+
if (statement.type === "ExportAllDeclaration") {
|
|
195
|
+
const node = statement as { exported?: unknown; source?: { value?: string } };
|
|
196
|
+
if (node.exported || !node.source?.value) continue;
|
|
197
|
+
for (const exported of collectPublicFunctions(
|
|
198
|
+
resolveModuleSpecifier(file, node.source.value),
|
|
199
|
+
visiting,
|
|
200
|
+
).values()) {
|
|
201
|
+
add(exported);
|
|
202
|
+
}
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (statement.type !== "ExportNamedDeclaration") continue;
|
|
206
|
+
const node = statement as {
|
|
207
|
+
declaration?: FunctionNode | null;
|
|
208
|
+
source?: { value?: string } | null;
|
|
209
|
+
specifiers?: {
|
|
210
|
+
local?: { name?: string; value?: string };
|
|
211
|
+
exported?: { name?: string; value?: string };
|
|
212
|
+
exportKind?: "value" | "type";
|
|
213
|
+
}[];
|
|
214
|
+
exportKind?: "value" | "type";
|
|
215
|
+
};
|
|
216
|
+
if (
|
|
217
|
+
node.declaration?.type === "FunctionDeclaration" ||
|
|
218
|
+
node.declaration?.type === "TSDeclareFunction"
|
|
219
|
+
) {
|
|
220
|
+
add(parseFunctionNode(file, node.declaration));
|
|
221
|
+
}
|
|
222
|
+
if (node.exportKind === "type") continue;
|
|
223
|
+
if (node.source?.value) {
|
|
224
|
+
const available = collectPublicFunctions(
|
|
225
|
+
resolveModuleSpecifier(file, node.source.value),
|
|
226
|
+
visiting,
|
|
227
|
+
);
|
|
228
|
+
for (const specifier of node.specifiers ?? []) {
|
|
229
|
+
if (specifier.exportKind === "type") continue;
|
|
230
|
+
const local = specifier.local?.name ?? specifier.local?.value;
|
|
231
|
+
const exportedName = specifier.exported?.name ?? specifier.exported?.value;
|
|
232
|
+
const target = local ? available.get(local) : undefined;
|
|
233
|
+
if (target && exportedName)
|
|
234
|
+
add({
|
|
235
|
+
name: exportedName,
|
|
236
|
+
source: target.source,
|
|
237
|
+
sourceFile: target.sourceFile,
|
|
238
|
+
sourceName: target.sourceName,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
for (const specifier of node.specifiers ?? []) {
|
|
244
|
+
if (specifier.exportKind === "type") continue;
|
|
245
|
+
const local = specifier.local?.name ?? specifier.local?.value;
|
|
246
|
+
const exportedName = specifier.exported?.name ?? specifier.exported?.value;
|
|
247
|
+
const target = local ? localFunctions.get(local) : undefined;
|
|
248
|
+
if (target && exportedName) {
|
|
249
|
+
const parsed = parseFunctionNode(file, target);
|
|
250
|
+
add({
|
|
251
|
+
name: exportedName,
|
|
252
|
+
source: parsed.source,
|
|
253
|
+
sourceFile: parsed.sourceFile,
|
|
254
|
+
sourceName: parsed.sourceName,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
visiting.delete(file);
|
|
261
|
+
return functions;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
interface FunctionNode {
|
|
265
|
+
readonly type: string;
|
|
266
|
+
readonly id?: { readonly name?: string } | null;
|
|
267
|
+
readonly generator?: boolean;
|
|
268
|
+
readonly declare?: boolean;
|
|
269
|
+
readonly body?: unknown;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function parseFunctionNode(file: string, node: FunctionNode): ResolvedFunctionExport {
|
|
273
|
+
const name = node.id?.name;
|
|
274
|
+
if (!name) throw new Error(`Anonymous exported function is not supported in ${file}`);
|
|
275
|
+
if (node.generator) throw new Error(`Generator export ${name} is not supported in ${file}`);
|
|
276
|
+
if (node.type === "TSDeclareFunction" || node.declare || !node.body) {
|
|
277
|
+
throw new Error(`Declaration-only function export ${name} is not supported in ${file}`);
|
|
278
|
+
}
|
|
279
|
+
return { name, source: `${file}#${name}`, sourceFile: file, sourceName: name };
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function resolveModuleSpecifier(importer: string, specifier: string): string {
|
|
283
|
+
if (!specifier.startsWith(".")) return createRequire(importer).resolve(specifier);
|
|
284
|
+
const base = resolve(dirname(importer), specifier);
|
|
285
|
+
const extension = extname(base);
|
|
286
|
+
const candidates = extension
|
|
287
|
+
? [
|
|
288
|
+
base,
|
|
289
|
+
...(extension === ".js" || extension === ".jsx"
|
|
290
|
+
? [`${base.slice(0, -extension.length)}.ts`, `${base.slice(0, -extension.length)}.tsx`]
|
|
291
|
+
: []),
|
|
292
|
+
]
|
|
293
|
+
: [
|
|
294
|
+
`${base}.ts`,
|
|
295
|
+
`${base}.tsx`,
|
|
296
|
+
`${base}.js`,
|
|
297
|
+
`${base}.jsx`,
|
|
298
|
+
`${base}.mjs`,
|
|
299
|
+
`${base}.cjs`,
|
|
300
|
+
resolve(base, "index.ts"),
|
|
301
|
+
resolve(base, "index.tsx"),
|
|
302
|
+
resolve(base, "index.js"),
|
|
303
|
+
];
|
|
304
|
+
for (const candidate of candidates) {
|
|
305
|
+
if (existsSync(candidate)) return candidate;
|
|
306
|
+
}
|
|
307
|
+
throw new Error(`Could not resolve ${specifier} from ${importer}`);
|
|
308
|
+
}
|
package/src/openapi.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* OpenAPI generator for tsoa controllers
|
|
2
|
+
* OpenAPI generator for tsoa controllers.
|
|
3
3
|
*
|
|
4
|
-
* TypeScript packages are discovered from tsoa imports
|
|
5
|
-
* recorded by `DBXToolsRustWorkspace`. Both feed the same optimized generated
|
|
6
|
-
* package:
|
|
4
|
+
* TypeScript packages are discovered from tsoa imports and feed the optimized generated package:
|
|
7
5
|
*
|
|
8
6
|
* - `openapi.json` - the OpenAPI 3 spec (tsoa `generateSpec`, then Speakeasy
|
|
9
7
|
* optimization to extract duplicate inline schemas into components).
|
|
@@ -48,7 +46,6 @@ import {
|
|
|
48
46
|
toPosix,
|
|
49
47
|
recordedPackages,
|
|
50
48
|
} from "./packages.ts";
|
|
51
|
-
import type { RustOpenApiMapping, RustWorkspaceMapping } from "./project-rs.ts";
|
|
52
49
|
import type { ReleaseUnitGraph } from "./release-catalog.ts";
|
|
53
50
|
import { readWorkspaceVersion } from "./workspace-version.ts";
|
|
54
51
|
|
|
@@ -100,15 +97,6 @@ function controllerPackages(): object.Sequence<RecordedPackage> {
|
|
|
100
97
|
return object.sequence(recordedPackages()).filter(isTsoaCandidate).filter(hasTsoaControllers);
|
|
101
98
|
}
|
|
102
99
|
|
|
103
|
-
function rustOpenapiMappings(): RustOpenApiMapping[] {
|
|
104
|
-
const manifest = JSON.parse(readFileSync(join(repoRoot, "package.json"), "utf8")) as unknown;
|
|
105
|
-
if (!object.isRecord(manifest)) return [];
|
|
106
|
-
const config = manifest.dbxToolsConfig;
|
|
107
|
-
if (!object.isRecord(config) || !object.isRecord(config.rust)) return [];
|
|
108
|
-
const mapping = config.rust as unknown as Partial<RustWorkspaceMapping>;
|
|
109
|
-
return Array.isArray(mapping.openapi) ? [...mapping.openapi] : [];
|
|
110
|
-
}
|
|
111
|
-
|
|
112
100
|
function inside(path: string, directory: string): boolean {
|
|
113
101
|
const child = relative(resolve(directory), resolve(path));
|
|
114
102
|
return child === "" || (!child.startsWith("..") && !isAbsolute(child));
|
|
@@ -120,16 +108,11 @@ export function isOpenapiProducerSource(path: string, producers: readonly string
|
|
|
120
108
|
return object.sequence(producers).some((producer) => inside(absolute, producer));
|
|
121
109
|
}
|
|
122
110
|
|
|
123
|
-
|
|
124
|
-
return [resolve(repoRoot, mapping.rust, "src"), resolve(repoRoot, mapping.rust, "Cargo.toml")];
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/** Source roots consumed by TypeScript and Rust OpenAPI producers. */
|
|
111
|
+
/** Source roots consumed by TypeScript OpenAPI producers. */
|
|
128
112
|
export function openapiWatchRoots(): string[] {
|
|
129
113
|
return [
|
|
130
114
|
...controllerPackages()
|
|
131
115
|
.map((pkg) => resolve(tsoaSource(pkg)))
|
|
132
|
-
.join(object.sequence(rustOpenapiMappings()).flatMap(rustOpenapiSources))
|
|
133
116
|
.distinct(),
|
|
134
117
|
];
|
|
135
118
|
}
|
|
@@ -139,14 +122,6 @@ export function isOpenapiSource(path: string): boolean {
|
|
|
139
122
|
const absolute = resolve(path);
|
|
140
123
|
const segments = toPosix(relative(repoRoot, absolute)).split("/");
|
|
141
124
|
if (segments.includes(OPENAPI_TAG)) return false;
|
|
142
|
-
if (
|
|
143
|
-
object
|
|
144
|
-
.sequence(rustOpenapiMappings())
|
|
145
|
-
.flatMap(rustOpenapiSources)
|
|
146
|
-
.some((source) => inside(absolute, source))
|
|
147
|
-
) {
|
|
148
|
-
return true;
|
|
149
|
-
}
|
|
150
125
|
return object
|
|
151
126
|
.sequence(recordedPackages())
|
|
152
127
|
.filter(isTsoaCandidate)
|
|
@@ -196,28 +171,6 @@ export async function optimizeOpenapiSpec(specPath: string, executable?: string)
|
|
|
196
171
|
await execFileAsync(openapi, ["spec", "optimize", specPath, "--write", "--non-interactive"]);
|
|
197
172
|
}
|
|
198
173
|
|
|
199
|
-
/** Cargo arguments that export one Rust producer's aide document. */
|
|
200
|
-
export function rustOpenapiArgs(
|
|
201
|
-
mapping: RustOpenApiMapping,
|
|
202
|
-
output: string,
|
|
203
|
-
targetDir = join(repoRoot, "target/openapi"),
|
|
204
|
-
): string[] {
|
|
205
|
-
return [
|
|
206
|
-
"run",
|
|
207
|
-
"--quiet",
|
|
208
|
-
"--target-dir",
|
|
209
|
-
targetDir,
|
|
210
|
-
"--package",
|
|
211
|
-
mapping.crate,
|
|
212
|
-
...(mapping.noDefaultFeatures ? ["--no-default-features"] : []),
|
|
213
|
-
...(mapping.features.length ? ["--features", mapping.features.join(",")] : []),
|
|
214
|
-
...(mapping.binary ? ["--bin", mapping.binary] : []),
|
|
215
|
-
"--",
|
|
216
|
-
"--generate-spec",
|
|
217
|
-
output,
|
|
218
|
-
];
|
|
219
|
-
}
|
|
220
|
-
|
|
221
174
|
type OpenApiTypeTools = {
|
|
222
175
|
openapiTS: typeof import("openapi-typescript").default;
|
|
223
176
|
astToString: typeof import("openapi-typescript").astToString;
|
|
@@ -257,11 +210,10 @@ async function writeClientPackage(
|
|
|
257
210
|
*/
|
|
258
211
|
export async function generateOpenapi(): Promise<string[]> {
|
|
259
212
|
const pkgs = controllerPackages().toArray();
|
|
260
|
-
const rustMappings = rustOpenapiMappings();
|
|
261
213
|
// Same reasoning as codegen's empty case: a workspace with no tsoa controllers
|
|
262
214
|
// is not a condition worth a line on every synth.
|
|
263
|
-
if (pkgs.length === 0
|
|
264
|
-
logger.debug("no TypeScript
|
|
215
|
+
if (pkgs.length === 0) {
|
|
216
|
+
logger.debug("no TypeScript OpenAPI producers found");
|
|
265
217
|
return [];
|
|
266
218
|
}
|
|
267
219
|
|
|
@@ -341,24 +293,5 @@ export async function generateOpenapi(): Promise<string[]> {
|
|
|
341
293
|
written.push(outDir);
|
|
342
294
|
logger.success(`openapi/${leaf} (from ${p.relPath})`);
|
|
343
295
|
}
|
|
344
|
-
for (const mapping of rustMappings) {
|
|
345
|
-
const outDir = resolve(repoRoot, mapping.output);
|
|
346
|
-
mkdirSync(outDir, { recursive: true });
|
|
347
|
-
const specPath = join(outDir, "openapi.json");
|
|
348
|
-
const tempDir = mkdtempSync(join(outDir, ".openapi-"));
|
|
349
|
-
const tempSpecPath = join(tempDir, "openapi.json");
|
|
350
|
-
try {
|
|
351
|
-
await execFileAsync("cargo", rustOpenapiArgs(mapping, tempSpecPath), { cwd: repoRoot });
|
|
352
|
-
await optimizeOpenapiSpec(tempSpecPath);
|
|
353
|
-
makeWritable(specPath);
|
|
354
|
-
renameSync(tempSpecPath, specPath);
|
|
355
|
-
} finally {
|
|
356
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
357
|
-
}
|
|
358
|
-
makeReadonly(specPath);
|
|
359
|
-
await writeClientPackage(outDir, `the aide routes in ${mapping.rust}`, tools);
|
|
360
|
-
written.push(outDir);
|
|
361
|
-
logger.success(`${mapping.output} (from ${mapping.rust})`);
|
|
362
|
-
}
|
|
363
296
|
return written;
|
|
364
297
|
}
|
package/src/packages.ts
CHANGED
|
@@ -291,6 +291,19 @@ export function syncResynthPaths(projectRoot: string = resolveRepoRoot()): strin
|
|
|
291
291
|
return Array.isArray(paths) ? stringUtils.parseList(paths.map((p) => String(p))) : [];
|
|
292
292
|
}
|
|
293
293
|
|
|
294
|
+
/** Python projects with Node bindings registered for the root sync watcher. */
|
|
295
|
+
export function pythonNodeBindingProjects(projectRoot: string = resolveRepoRoot()): string[] {
|
|
296
|
+
const configured = readDbxToolsConfig(projectRoot)?.pythonNodeBindings;
|
|
297
|
+
if (!Array.isArray(configured)) return [];
|
|
298
|
+
return [
|
|
299
|
+
...new Set(
|
|
300
|
+
configured.flatMap((value) =>
|
|
301
|
+
typeof value === "string" && value.trim() ? [value.trim()] : [],
|
|
302
|
+
),
|
|
303
|
+
),
|
|
304
|
+
].sort();
|
|
305
|
+
}
|
|
306
|
+
|
|
294
307
|
/**
|
|
295
308
|
* The recorded workspace members from `pnpm-workspace.yaml` (the source of truth),
|
|
296
309
|
* each augmented with the `tags` read back from its `package.json`. This is what
|
|
@@ -314,6 +327,38 @@ export function recordedPackages(projectRoot: string = resolveRepoRoot()): Recor
|
|
|
314
327
|
return out.sort((a, b) => a.path.localeCompare(b.path));
|
|
315
328
|
}
|
|
316
329
|
|
|
330
|
+
/**
|
|
331
|
+
* Absolute directories for a workspace package and its transitive runtime
|
|
332
|
+
* workspace dependencies. Dependency edges come from package manifests; external
|
|
333
|
+
* packages are ignored. Returns `[]` when `packageName` is not a workspace member.
|
|
334
|
+
*/
|
|
335
|
+
export function workspaceDependencyDirectories(
|
|
336
|
+
packageName: string,
|
|
337
|
+
projectRoot: string = resolveRepoRoot(),
|
|
338
|
+
): string[] {
|
|
339
|
+
const packages = new Map<string, { dir: string; manifest: Record<string, unknown> }>();
|
|
340
|
+
for (const pkg of recordedPackages(projectRoot)) {
|
|
341
|
+
const manifest = readPackageManifest(pkg.dir);
|
|
342
|
+
if (!manifest || typeof manifest.name !== "string") continue;
|
|
343
|
+
packages.set(manifest.name, { dir: pkg.dir, manifest });
|
|
344
|
+
}
|
|
345
|
+
if (!packages.has(packageName)) return [];
|
|
346
|
+
|
|
347
|
+
const directories = new Set<string>();
|
|
348
|
+
const visit = (name: string): void => {
|
|
349
|
+
const pkg = packages.get(name);
|
|
350
|
+
if (!pkg || directories.has(pkg.dir)) return;
|
|
351
|
+
directories.add(pkg.dir);
|
|
352
|
+
for (const field of ["dependencies", "optionalDependencies", "peerDependencies"] as const) {
|
|
353
|
+
const dependencies = pkg.manifest[field];
|
|
354
|
+
if (!object.isRecord(dependencies)) continue;
|
|
355
|
+
for (const dependency of Object.keys(dependencies)) visit(dependency);
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
visit(packageName);
|
|
359
|
+
return [...directories].sort();
|
|
360
|
+
}
|
|
361
|
+
|
|
317
362
|
/**
|
|
318
363
|
* The configured roots recorded in the root manifest. Older workspaces without
|
|
319
364
|
* that field fall back to the distinct first segment of every recorded member,
|
package/src/project-js.ts
CHANGED
|
@@ -48,7 +48,7 @@ import {
|
|
|
48
48
|
type ReleaseDependencyInput,
|
|
49
49
|
type ReleaseUnitRule,
|
|
50
50
|
} from "./release-catalog.ts";
|
|
51
|
-
import { DBXToolsRelease, type ReleaseDocsOptions
|
|
51
|
+
import { DBXToolsRelease, type ReleaseDocsOptions } from "./release.ts";
|
|
52
52
|
import { AGNOSTIC_COMPILER_OPTIONS, PACKAGE_TAG_MIXINS, type PackageTag } from "./tags.ts";
|
|
53
53
|
import { DBXToolsRootTsconfig } from "./tsconfig.ts";
|
|
54
54
|
import { DBXToolsVsCode } from "./vscode.ts";
|
|
@@ -384,7 +384,7 @@ const PRETTIER_SETTINGS: javascript.PrettierSettings = {
|
|
|
384
384
|
|
|
385
385
|
export { PROJEN_VERSION } from "./projen-version.ts";
|
|
386
386
|
|
|
387
|
-
/** SPDX license shared by generated JavaScript
|
|
387
|
+
/** SPDX license shared by generated JavaScript and Python packages. */
|
|
388
388
|
export const DBX_TOOLS_LICENSE = "Apache-2.0";
|
|
389
389
|
|
|
390
390
|
/**
|
|
@@ -625,8 +625,6 @@ export type DBXToolsJavaScriptProjectOptions = CommonProjectOptions &
|
|
|
625
625
|
readonly releasePythonRoot?: string;
|
|
626
626
|
/** Repository task names run during local release preparation before publication. */
|
|
627
627
|
readonly releaseValidationTasks?: readonly string[];
|
|
628
|
-
/** Optional AI-generated release summary. Defaults to enabled. */
|
|
629
|
-
readonly releaseSummary?: boolean | ReleaseSummaryOptions;
|
|
630
628
|
/** Set to `false` to omit normal npm workspace publication. */
|
|
631
629
|
readonly nodeRelease?: boolean;
|
|
632
630
|
/** Unified dbx-tools release workflow, or no release surface. Defaults to `dbx-tools`. */
|
|
@@ -1324,7 +1322,7 @@ class PublishedPackageDescriptionValidation extends Component {
|
|
|
1324
1322
|
* fail with ERR_MODULE_NOT_FOUND after such an update. bun runs the `.ts`
|
|
1325
1323
|
* directly (no tsx, no build step).
|
|
1326
1324
|
*/
|
|
1327
|
-
export function taskScript(_project:
|
|
1325
|
+
export function taskScript(_project: Project, script: string, args = ""): string {
|
|
1328
1326
|
const scriptPath = toPosix(join("node_modules", "@dbx-tools", "projen", "tasks", script));
|
|
1329
1327
|
return args ? `bun ${scriptPath} ${args}` : `bun ${scriptPath}`;
|
|
1330
1328
|
}
|
|
@@ -1580,7 +1578,6 @@ function initProject(
|
|
|
1580
1578
|
docs: options.releaseDocs,
|
|
1581
1579
|
pythonRoot: options.releasePythonRoot,
|
|
1582
1580
|
validationTasks: options.releaseValidationTasks,
|
|
1583
|
-
summary: options.releaseSummary,
|
|
1584
1581
|
});
|
|
1585
1582
|
}
|
|
1586
1583
|
}
|
package/src/project-predicate.ts
CHANGED
|
@@ -33,8 +33,7 @@ export function isDBXToolsProject(): Predicate<IConstruct, DBXToolsProject> {
|
|
|
33
33
|
return isProject().and(
|
|
34
34
|
(project): project is DBXToolsProject =>
|
|
35
35
|
(project as Partial<DBXToolsProject>).language === "javascript" ||
|
|
36
|
-
(project as Partial<DBXToolsProject>).language === "python"
|
|
37
|
-
(project as Partial<DBXToolsProject>).language === "rust",
|
|
36
|
+
(project as Partial<DBXToolsProject>).language === "python",
|
|
38
37
|
);
|
|
39
38
|
}
|
|
40
39
|
|