@dbx-tools/projen 0.9.32 → 0.9.36
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 +71 -65
- package/index.ts +5 -8
- package/package.json +8 -7
- package/src/_task-command.ts +0 -75
- package/src/barrels.ts +15 -23
- package/src/bun-app.ts +5 -6
- package/src/clean.ts +41 -15
- package/src/codegen.ts +2 -3
- package/src/dbx-tools-config.ts +23 -64
- package/src/generated.ts +7 -1
- package/src/module-exports.ts +35 -3
- package/src/openapi.ts +34 -38
- package/src/packages.ts +3 -30
- package/src/pnpm-workspace.ts +21 -26
- package/src/project-js.ts +152 -365
- package/src/project-predicate.ts +4 -5
- package/src/project-py.ts +55 -144
- package/src/publish.ts +1 -1
- package/src/python-node-bindings.ts +140 -67
- package/src/python-node-bundle.ts +6 -42
- package/src/release-context.ts +4 -4
- package/src/release.ts +88 -86
- package/src/tags.ts +20 -18
- package/src/tsconfig.ts +9 -5
- package/src/vscode.ts +47 -58
- package/src/watch.ts +2 -8
- package/src/workspace-version.ts +1 -6
- package/tasks/barrels.ts +43 -38
- package/tasks/bump.ts +24 -20
- package/tasks/clean.ts +54 -50
- package/tasks/compile-workspace.ts +2 -117
- package/tasks/lib/compile-workspace.ts +119 -0
- package/tasks/lib/local-publish.ts +122 -0
- package/tasks/lib/publish-npm.ts +458 -0
- package/tasks/lib/publish-python.ts +195 -0
- package/tasks/lib/publish.ts +264 -0
- package/tasks/lib/python-node-bindings.ts +1262 -0
- package/tasks/{python-registry.ts → lib/python-registry.ts} +21 -30
- package/tasks/{python-release.ts → lib/python-release.ts} +1 -0
- package/tasks/lib/release-version.ts +66 -0
- package/tasks/lib/release.ts +212 -0
- package/tasks/local-publish.ts +4 -118
- package/tasks/openapi.ts +25 -21
- package/tasks/projenrc.ts +14 -12
- package/tasks/publish-npm.ts +3 -364
- package/tasks/publish-python.ts +3 -184
- package/tasks/publish.ts +3 -279
- package/tasks/python-node-bindings.ts +3 -1014
- package/tasks/release-version.ts +3 -62
- package/tasks/release.ts +3 -121
- package/tasks/sync.ts +71 -70
- package/tasks/version-check.ts +3 -10
- package/src/release-catalog.ts +0 -684
- package/tasks/databricks-cli-assets.ts +0 -129
- package/tasks/python-node-bindings-watch.ts +0 -47
package/src/dbx-tools-config.ts
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Values are read and written on the component's object; each write flushes the
|
|
5
|
-
* record to the manifest through `project.package.addField`.
|
|
6
|
-
*/
|
|
7
|
-
import { object } from "@dbx-tools/shared-core";
|
|
8
|
-
import { Component, javascript } from "projen";
|
|
1
|
+
/** Typed `package.json` `dbxToolsConfig` record for a package. */
|
|
2
|
+
import { javascript } from "projen";
|
|
9
3
|
|
|
10
4
|
/** `package.json` field name for the dbx-tools config object. */
|
|
11
5
|
const DBX_TOOLS_CONFIG_KEY = "dbxToolsConfig";
|
|
@@ -16,70 +10,35 @@ export interface DBXToolsConfigOptions {
|
|
|
16
10
|
readonly tags?: string[];
|
|
17
11
|
}
|
|
18
12
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const config = manifest[DBX_TOOLS_CONFIG_KEY];
|
|
24
|
-
if (object.isRecord(config)) {
|
|
25
|
-
return config;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
} catch {}
|
|
29
|
-
return {};
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function loadConfig(dbxToolsConfig: DBXToolsConfig, pkg: javascript.NodePackage) {
|
|
33
|
-
const { tags, ...config } = readDBXToolsConfig(pkg);
|
|
34
|
-
if (Array.isArray(tags)) {
|
|
35
|
-
object
|
|
36
|
-
.sequence(tags)
|
|
37
|
-
.filter((v: unknown) => typeof v === "string")
|
|
38
|
-
.forEach((v: string) => dbxToolsConfig.tags.push(v));
|
|
39
|
-
}
|
|
40
|
-
Object.entries(config).forEach(([key, value]) => {
|
|
41
|
-
dbxToolsConfig[key] = value;
|
|
42
|
-
});
|
|
13
|
+
interface DBXToolsConfigData {
|
|
14
|
+
readonly tags?: string[];
|
|
15
|
+
readonly packageRoots?: string[];
|
|
16
|
+
readonly syncResynthPaths?: string[];
|
|
43
17
|
}
|
|
44
18
|
|
|
45
|
-
/**
|
|
46
|
-
|
|
47
|
-
* directly - push to {@link DBXToolsConfig.tags}, or assign any other key
|
|
48
|
-
* through the index signature. Every own key (i.e. not inherited from
|
|
49
|
-
* `Component`) is collected by {@link DBXToolsConfig.data} and written through
|
|
50
|
-
* `project.package.addField` at synth, so nothing is cached on a field.
|
|
51
|
-
*/
|
|
52
|
-
export class DBXToolsConfig extends Component {
|
|
53
|
-
private readonly inheritedKeys: ReadonlySet<string>;
|
|
19
|
+
/** Owns the typed in-memory `dbxToolsConfig` written to `package.json`. */
|
|
20
|
+
export class DBXToolsConfig {
|
|
54
21
|
readonly tags: string[];
|
|
55
|
-
|
|
22
|
+
packageRoots?: string[];
|
|
23
|
+
syncResynthPaths?: string[];
|
|
56
24
|
|
|
57
25
|
constructor(
|
|
58
|
-
|
|
26
|
+
readonly project: javascript.NodeProject,
|
|
59
27
|
options: DBXToolsConfigOptions = {},
|
|
60
28
|
) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
data(): Record<string, unknown> {
|
|
69
|
-
const dataRecord: Record<string, unknown> = {};
|
|
70
|
-
for (const [key, value] of Object.entries(this)) {
|
|
71
|
-
if (this.inheritedKeys.has(key)) continue;
|
|
72
|
-
dataRecord[key] = value;
|
|
73
|
-
}
|
|
74
|
-
dataRecord.tags = object.sequence(this.tags).distinct().toArray();
|
|
75
|
-
return dataRecord;
|
|
29
|
+
this.tags = [...(options.tags ?? [])];
|
|
30
|
+
project.package.addField(DBX_TOOLS_CONFIG_KEY, () => {
|
|
31
|
+
const data = this.data();
|
|
32
|
+
return Object.keys(data).length === 0 ? undefined : data;
|
|
33
|
+
});
|
|
76
34
|
}
|
|
77
35
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
36
|
+
data(): DBXToolsConfigData {
|
|
37
|
+
const tags = [...new Set(this.tags)];
|
|
38
|
+
return {
|
|
39
|
+
...(tags.length ? { tags } : {}),
|
|
40
|
+
...(this.packageRoots?.length ? { packageRoots: this.packageRoots } : {}),
|
|
41
|
+
...(this.syncResynthPaths?.length ? { syncResynthPaths: this.syncResynthPaths } : {}),
|
|
42
|
+
};
|
|
84
43
|
}
|
|
85
44
|
}
|
package/src/generated.ts
CHANGED
|
@@ -66,7 +66,13 @@ export function header(opts: HeaderOpts): string {
|
|
|
66
66
|
*/
|
|
67
67
|
export function isGenerated(file: string): boolean {
|
|
68
68
|
try {
|
|
69
|
-
|
|
69
|
+
const source = readFileSync(file, "utf8");
|
|
70
|
+
return (
|
|
71
|
+
source.startsWith(MARKER) ||
|
|
72
|
+
source.startsWith("// ~~ Generated by projen") ||
|
|
73
|
+
source.startsWith("# ~~ Generated by projen") ||
|
|
74
|
+
/"\/\/"\s*:\s*"~~ Generated by projen/.test(source)
|
|
75
|
+
);
|
|
70
76
|
} catch {
|
|
71
77
|
return false;
|
|
72
78
|
}
|
package/src/module-exports.ts
CHANGED
|
@@ -69,6 +69,7 @@ export interface PublicFunctionExport {
|
|
|
69
69
|
readonly name: string;
|
|
70
70
|
readonly sourceFile: string;
|
|
71
71
|
readonly sourceName: string;
|
|
72
|
+
readonly async: boolean;
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
/** Declaration node types that are inherently type-only. */
|
|
@@ -149,10 +150,31 @@ export function moduleExports(file: string): ModuleExport[] {
|
|
|
149
150
|
*/
|
|
150
151
|
export function publicFunctionExports(file: string): PublicFunctionExport[] {
|
|
151
152
|
return [...collectPublicFunctions(resolve(file), new Set()).values()]
|
|
152
|
-
.map(({ name, sourceFile, sourceName }) => ({
|
|
153
|
+
.map(({ name, sourceFile, sourceName, async }) => ({
|
|
154
|
+
name,
|
|
155
|
+
sourceFile,
|
|
156
|
+
sourceName,
|
|
157
|
+
async,
|
|
158
|
+
}))
|
|
153
159
|
.sort((left, right) => left.name.localeCompare(right.name));
|
|
154
160
|
}
|
|
155
161
|
|
|
162
|
+
/** Names of namespace exports declared as `export * as name from "..."`. */
|
|
163
|
+
export function publicNamespaceExports(file: string): string[] {
|
|
164
|
+
return moduleStatements(file)
|
|
165
|
+
.flatMap((statement) => {
|
|
166
|
+
if (statement.type !== "ExportAllDeclaration") return [];
|
|
167
|
+
const exported = (
|
|
168
|
+
statement as {
|
|
169
|
+
exported?: { name?: string; value?: string } | null;
|
|
170
|
+
}
|
|
171
|
+
).exported;
|
|
172
|
+
const name = exported?.name ?? exported?.value;
|
|
173
|
+
return name ? [name] : [];
|
|
174
|
+
})
|
|
175
|
+
.sort((left, right) => left.localeCompare(right));
|
|
176
|
+
}
|
|
177
|
+
|
|
156
178
|
interface ResolvedFunctionExport extends PublicFunctionExport {
|
|
157
179
|
readonly source: string;
|
|
158
180
|
}
|
|
@@ -230,13 +252,15 @@ function collectPublicFunctions(
|
|
|
230
252
|
const local = specifier.local?.name ?? specifier.local?.value;
|
|
231
253
|
const exportedName = specifier.exported?.name ?? specifier.exported?.value;
|
|
232
254
|
const target = local ? available.get(local) : undefined;
|
|
233
|
-
if (target && exportedName)
|
|
255
|
+
if (target && exportedName) {
|
|
234
256
|
add({
|
|
235
257
|
name: exportedName,
|
|
236
258
|
source: target.source,
|
|
237
259
|
sourceFile: target.sourceFile,
|
|
238
260
|
sourceName: target.sourceName,
|
|
261
|
+
async: target.async,
|
|
239
262
|
});
|
|
263
|
+
}
|
|
240
264
|
}
|
|
241
265
|
continue;
|
|
242
266
|
}
|
|
@@ -252,6 +276,7 @@ function collectPublicFunctions(
|
|
|
252
276
|
source: parsed.source,
|
|
253
277
|
sourceFile: parsed.sourceFile,
|
|
254
278
|
sourceName: parsed.sourceName,
|
|
279
|
+
async: parsed.async,
|
|
255
280
|
});
|
|
256
281
|
}
|
|
257
282
|
}
|
|
@@ -267,6 +292,7 @@ interface FunctionNode {
|
|
|
267
292
|
readonly generator?: boolean;
|
|
268
293
|
readonly declare?: boolean;
|
|
269
294
|
readonly body?: unknown;
|
|
295
|
+
readonly async?: boolean;
|
|
270
296
|
}
|
|
271
297
|
|
|
272
298
|
function parseFunctionNode(file: string, node: FunctionNode): ResolvedFunctionExport {
|
|
@@ -276,7 +302,13 @@ function parseFunctionNode(file: string, node: FunctionNode): ResolvedFunctionEx
|
|
|
276
302
|
if (node.type === "TSDeclareFunction" || node.declare || !node.body) {
|
|
277
303
|
throw new Error(`Declaration-only function export ${name} is not supported in ${file}`);
|
|
278
304
|
}
|
|
279
|
-
return {
|
|
305
|
+
return {
|
|
306
|
+
name,
|
|
307
|
+
source: `${file}#${name}`,
|
|
308
|
+
sourceFile: file,
|
|
309
|
+
sourceName: name,
|
|
310
|
+
async: node.async === true,
|
|
311
|
+
};
|
|
280
312
|
}
|
|
281
313
|
|
|
282
314
|
function resolveModuleSpecifier(importer: string, specifier: string): string {
|
package/src/openapi.ts
CHANGED
|
@@ -20,20 +20,11 @@
|
|
|
20
20
|
* consuming workspace, which is where they already live. Speakeasy's `openapi`
|
|
21
21
|
* binary is installed lazily through `@dbx-tools/core`'s binary cache.
|
|
22
22
|
*/
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
existsSync,
|
|
26
|
-
mkdirSync,
|
|
27
|
-
mkdtempSync,
|
|
28
|
-
readFileSync,
|
|
29
|
-
renameSync,
|
|
30
|
-
rmSync,
|
|
31
|
-
writeFileSync,
|
|
32
|
-
} from "node:fs";
|
|
23
|
+
import { mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
33
24
|
import { createRequire } from "node:module";
|
|
34
25
|
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
35
|
-
import { promisify } from "node:util";
|
|
36
26
|
import * as bin from "@dbx-tools/core/bin";
|
|
27
|
+
import * as exec from "@dbx-tools/core/exec";
|
|
37
28
|
import { find } from "@dbx-tools/path";
|
|
38
29
|
import { log, object, stringUtils } from "@dbx-tools/shared-core";
|
|
39
30
|
import type * as ts from "typescript";
|
|
@@ -46,7 +37,6 @@ import {
|
|
|
46
37
|
toPosix,
|
|
47
38
|
recordedPackages,
|
|
48
39
|
} from "./packages.ts";
|
|
49
|
-
import type { ReleaseUnitGraph } from "./release-catalog.ts";
|
|
50
40
|
import { readWorkspaceVersion } from "./workspace-version.ts";
|
|
51
41
|
|
|
52
42
|
const logger = log.logger("projen:openapi");
|
|
@@ -57,7 +47,6 @@ const OPENAPI_TAG = "openapi";
|
|
|
57
47
|
const TSOA_IMPORT = /from\s+['"](?:tsoa|@tsoa\/runtime)['"]/;
|
|
58
48
|
const SPEAKEASY_OPENAPI_VERSION = "1.24.0";
|
|
59
49
|
const SPEAKEASY_OPENAPI_RELEASE_URL = `https://github.com/speakeasy-api/openapi/releases/download/v${SPEAKEASY_OPENAPI_VERSION}`;
|
|
60
|
-
const execFileAsync = promisify(execFile);
|
|
61
50
|
|
|
62
51
|
// prettier-ignore
|
|
63
52
|
const CLIENT_SRC =
|
|
@@ -102,12 +91,6 @@ function inside(path: string, directory: string): boolean {
|
|
|
102
91
|
return child === "" || (!child.startsWith("..") && !isAbsolute(child));
|
|
103
92
|
}
|
|
104
93
|
|
|
105
|
-
/** Whether one changed source belongs to any producing package directory. */
|
|
106
|
-
export function isOpenapiProducerSource(path: string, producers: readonly string[]): boolean {
|
|
107
|
-
const absolute = resolve(path);
|
|
108
|
-
return object.sequence(producers).some((producer) => inside(absolute, producer));
|
|
109
|
-
}
|
|
110
|
-
|
|
111
94
|
/** Source roots consumed by TypeScript OpenAPI producers. */
|
|
112
95
|
export function openapiWatchRoots(): string[] {
|
|
113
96
|
return [
|
|
@@ -168,7 +151,10 @@ async function speakeasyOpenapiPath(): Promise<string> {
|
|
|
168
151
|
/** Deduplicate inline schemas into `components.schemas` with Speakeasy. */
|
|
169
152
|
export async function optimizeOpenapiSpec(specPath: string, executable?: string): Promise<void> {
|
|
170
153
|
const openapi = executable ?? (await speakeasyOpenapiPath());
|
|
171
|
-
await
|
|
154
|
+
await exec.spawn(openapi, ["spec", "optimize", specPath, "--write", "--non-interactive"], {
|
|
155
|
+
stdin: "ignore",
|
|
156
|
+
check: true,
|
|
157
|
+
});
|
|
172
158
|
}
|
|
173
159
|
|
|
174
160
|
type OpenApiTypeTools = {
|
|
@@ -234,28 +220,14 @@ export async function generateOpenapi(): Promise<string[]> {
|
|
|
234
220
|
const runtime = lazyRequire<typeof ts>(require, "typescript", "openapi generation");
|
|
235
221
|
return {
|
|
236
222
|
generateSpec,
|
|
237
|
-
|
|
238
|
-
experimentalDecorators: true,
|
|
239
|
-
target: runtime.ScriptTarget.ES2022,
|
|
240
|
-
module: runtime.ModuleKind.ESNext,
|
|
241
|
-
moduleResolution: runtime.ModuleResolutionKind.Bundler,
|
|
242
|
-
esModuleInterop: true,
|
|
243
|
-
skipLibCheck: true,
|
|
244
|
-
} satisfies ts.CompilerOptions,
|
|
223
|
+
runtime,
|
|
245
224
|
};
|
|
246
225
|
})()
|
|
247
226
|
: undefined;
|
|
248
227
|
|
|
249
|
-
const
|
|
250
|
-
const releaseGraph = existsSync(releaseGraphPath)
|
|
251
|
-
? (JSON.parse(readFileSync(releaseGraphPath, "utf8")) as ReleaseUnitGraph)
|
|
252
|
-
: undefined;
|
|
228
|
+
const version = readWorkspaceVersion(repoRoot);
|
|
253
229
|
const written: string[] = [];
|
|
254
230
|
for (const p of pkgs) {
|
|
255
|
-
const releaseProject = releaseGraph?.projects.find((project) => project.path === p.path);
|
|
256
|
-
const specVersion =
|
|
257
|
-
releaseGraph?.units.find((unit) => unit.id === releaseProject?.unit)?.version ??
|
|
258
|
-
readWorkspaceVersion(repoRoot);
|
|
259
231
|
// The generated package's folder is the source's leaf folder name (`api`), not
|
|
260
232
|
// its npm name - `p.name` is the (possibly-overridden) manifest name.
|
|
261
233
|
const leaf = p.relPath.split("/").pop() ?? p.relPath;
|
|
@@ -268,6 +240,30 @@ export async function generateOpenapi(): Promise<string[]> {
|
|
|
268
240
|
const tempDir = mkdtempSync(join(outDir, ".openapi-"));
|
|
269
241
|
const tempSpecPath = join(tempDir, "openapi.json");
|
|
270
242
|
try {
|
|
243
|
+
const tsconfigPath = join(p.dir, "tsconfig.json");
|
|
244
|
+
const config = typescript!.runtime.readConfigFile(
|
|
245
|
+
tsconfigPath,
|
|
246
|
+
typescript!.runtime.sys.readFile,
|
|
247
|
+
);
|
|
248
|
+
if (config.error) {
|
|
249
|
+
throw new Error(
|
|
250
|
+
typescript!.runtime.flattenDiagnosticMessageText(config.error.messageText, "\n"),
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
const parsed = typescript!.runtime.parseJsonConfigFileContent(
|
|
254
|
+
config.config,
|
|
255
|
+
typescript!.runtime.sys,
|
|
256
|
+
p.dir,
|
|
257
|
+
);
|
|
258
|
+
if (parsed.errors.length > 0) {
|
|
259
|
+
throw new Error(
|
|
260
|
+
parsed.errors
|
|
261
|
+
.map((error) =>
|
|
262
|
+
typescript!.runtime.flattenDiagnosticMessageText(error.messageText, "\n"),
|
|
263
|
+
)
|
|
264
|
+
.join("\n"),
|
|
265
|
+
);
|
|
266
|
+
}
|
|
271
267
|
await typescript!.generateSpec(
|
|
272
268
|
{
|
|
273
269
|
entryFile: "",
|
|
@@ -277,9 +273,9 @@ export async function generateOpenapi(): Promise<string[]> {
|
|
|
277
273
|
specFileBaseName: "openapi",
|
|
278
274
|
specVersion: 3,
|
|
279
275
|
name: `${p.relPath} API`,
|
|
280
|
-
version
|
|
276
|
+
version,
|
|
281
277
|
},
|
|
282
|
-
|
|
278
|
+
parsed.options,
|
|
283
279
|
);
|
|
284
280
|
await optimizeOpenapiSpec(tempSpecPath);
|
|
285
281
|
makeWritable(specPath);
|
package/src/packages.ts
CHANGED
|
@@ -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 { isGenerated } from "./generated.ts";
|
|
32
33
|
|
|
33
34
|
/** Resolve the nearest package/projenrc root at call time. */
|
|
34
35
|
export function resolveRepoRoot(cwd: string = process.cwd()): string {
|
|
@@ -86,24 +87,9 @@ function nestingTagsFromSegments(segments: readonly string[]): string[] {
|
|
|
86
87
|
/** Matches a barrel `index.<ext>` (as a basename or a posix path tail). */
|
|
87
88
|
const BARREL_RE = /(^|\/)index\.(ts|tsx|js|jsx|mjs|cjs)$/;
|
|
88
89
|
|
|
89
|
-
/**
|
|
90
|
-
const GENERATED_BASENAMES = new Set([
|
|
91
|
-
"package.json",
|
|
92
|
-
"tsconfig.json",
|
|
93
|
-
"tsconfig.dev.json",
|
|
94
|
-
"bunfig.toml",
|
|
95
|
-
"dev.ts",
|
|
96
|
-
"build.ts",
|
|
97
|
-
]);
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* True if the file matches the watcher's generated-file heuristic: projen manifest
|
|
101
|
-
* basenames, package-root barrels (`index.ts`), bun app scaffolding, or declaration
|
|
102
|
-
* files. Other read-only toolchain output (e.g. openapi artifacts) is not covered here.
|
|
103
|
-
*/
|
|
90
|
+
/** True when a durable generated-file marker identifies the file's owner. */
|
|
104
91
|
export function isGeneratedFile(file: string): boolean {
|
|
105
|
-
|
|
106
|
-
return GENERATED_BASENAMES.has(base) || BARREL_RE.test(base) || base.endsWith(".d.ts");
|
|
92
|
+
return isGenerated(file);
|
|
107
93
|
}
|
|
108
94
|
|
|
109
95
|
/** A re-exportable source module: ts/tsx/js/jsx/mjs/cjs, not a barrel/test/decl. */
|
|
@@ -291,19 +277,6 @@ export function syncResynthPaths(projectRoot: string = resolveRepoRoot()): strin
|
|
|
291
277
|
return Array.isArray(paths) ? stringUtils.parseList(paths.map((p) => String(p))) : [];
|
|
292
278
|
}
|
|
293
279
|
|
|
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
|
-
|
|
307
280
|
/**
|
|
308
281
|
* The recorded workspace members from `pnpm-workspace.yaml` (the source of truth),
|
|
309
282
|
* each augmented with the `tags` read back from its `package.json`. This is what
|
package/src/pnpm-workspace.ts
CHANGED
|
@@ -14,13 +14,11 @@
|
|
|
14
14
|
* `packages` straight through; nothing derives it from `project.subprojects`.
|
|
15
15
|
* The root's scan attaches packages AFTER construction, so the list is
|
|
16
16
|
* resolved in `preSynthesize`.
|
|
17
|
-
* - **The catalog is accumulated.**
|
|
18
|
-
*
|
|
17
|
+
* - **The catalog is accumulated.** Tag mixins and a consumer's `.projenrc.ts`
|
|
18
|
+
* add pins after construction, after Projen has captured the options object.
|
|
19
19
|
* - **Build allowances are the `allowBuilds` MAP.** projen's `allowScripts`
|
|
20
20
|
* renders `onlyBuiltDependencies`, and the pnpm this repo installs with (10.33)
|
|
21
|
-
* does not read that key at ALL - its only build gate is `allowBuilds
|
|
22
|
-
* projen's schema does not type. Rendering the list would install with every
|
|
23
|
-
* build script silently skipped.
|
|
21
|
+
* does not read that key at ALL - its only build gate is `allowBuilds`.
|
|
24
22
|
*
|
|
25
23
|
* Late mutation works because projen renders lazily (its `YamlFile` takes
|
|
26
24
|
* `obj: () => toJson_PnpmWorkspaceYamlSchema(options)`) and because
|
|
@@ -44,16 +42,12 @@ import { BUN_VERSION } from "./bun-workflow.ts";
|
|
|
44
42
|
import { toPosix } from "./packages.ts";
|
|
45
43
|
|
|
46
44
|
/**
|
|
47
|
-
* The pnpm `catalog:` version registry
|
|
48
|
-
* pnpm-workspace feature (packages reference it via a `catalog:` specifier), so
|
|
49
|
-
* there is no projen type for it - it's just a string map.
|
|
45
|
+
* The pnpm `catalog:` version registry typed by Projen's workspace schema.
|
|
50
46
|
*/
|
|
51
|
-
export type Catalog =
|
|
47
|
+
export type Catalog = NonNullable<javascript.PnpmWorkspaceYamlOptions["catalog"]>;
|
|
52
48
|
|
|
53
49
|
/**
|
|
54
|
-
*
|
|
55
|
-
* projen's schema, whose build gate is the `onlyBuiltDependencies` LIST that
|
|
56
|
-
* current pnpm ignores.
|
|
50
|
+
* Stronger local type for Projen's permissively typed `allowBuilds` field.
|
|
57
51
|
*/
|
|
58
52
|
export type AllowBuilds = Record<string, boolean>;
|
|
59
53
|
|
|
@@ -80,13 +74,11 @@ const DEFAULT_CATALOG: Catalog = {
|
|
|
80
74
|
// `@mastra/core`'s `^3.25.0 || ^4.0.0` peer range.
|
|
81
75
|
zod: "4.3.6",
|
|
82
76
|
typescript: "^5.9.3",
|
|
83
|
-
tsx: "^4.23.0",
|
|
84
77
|
commander: "^15.0.0",
|
|
85
78
|
"@clack/prompts": "^1.7.0",
|
|
86
79
|
"openapi-fetch": "^0.17.0",
|
|
87
80
|
tsoa: "^6.6.0",
|
|
88
81
|
concurrently: "^10.0.3",
|
|
89
|
-
pnpm: "^11.0.6",
|
|
90
82
|
"@databricks/appkit": "0.81.0",
|
|
91
83
|
"@databricks/appkit-ui": "0.81.0",
|
|
92
84
|
"@databricks/sdk-experimental": "^0.17.0",
|
|
@@ -125,7 +117,8 @@ const DEFAULT_OVERRIDES: Readonly<Record<string, string>> = {
|
|
|
125
117
|
/**
|
|
126
118
|
* pnpm settings this engine applies to every workspace, beyond members, catalog,
|
|
127
119
|
* and allowances. Each is stated because pnpm's own default is the weaker choice
|
|
128
|
-
* for a projen-managed monorepo;
|
|
120
|
+
* for a projen-managed monorepo; native `pnpmOptions.workspaceYamlOptions`
|
|
121
|
+
* override any.
|
|
129
122
|
*/
|
|
130
123
|
const DEFAULT_WORKSPACE_YAML: javascript.PnpmWorkspaceYamlOptions = {
|
|
131
124
|
// The catalog is GENERATED (`addCatalog` in `.projenrc.ts` / a tag mixin), so
|
|
@@ -150,8 +143,6 @@ export interface DBXToolsPNPMWorkspaceOptions {
|
|
|
150
143
|
readonly catalog?: Catalog;
|
|
151
144
|
/** Initial build allowances, merged over `{ esbuild: true }`. */
|
|
152
145
|
readonly allowBuilds?: AllowBuilds;
|
|
153
|
-
/** Any other pnpm-workspace setting, typed by projen's schema. */
|
|
154
|
-
readonly workspaceYaml?: javascript.PnpmWorkspaceYamlOptions;
|
|
155
146
|
}
|
|
156
147
|
|
|
157
148
|
/**
|
|
@@ -167,25 +158,26 @@ export class PnpmWorkspaceState {
|
|
|
167
158
|
* The options object handed to projen, whose members are mutated in place.
|
|
168
159
|
* Held as the live reference the native component captured.
|
|
169
160
|
*
|
|
170
|
-
* Widened with `allowBuilds`, which projen's schema type does not declare;
|
|
171
|
-
* projen renders unrecognized keys verbatim, so it reaches the file.
|
|
172
161
|
*/
|
|
173
|
-
readonly options: javascript.PnpmWorkspaceYamlOptions
|
|
162
|
+
readonly options: javascript.PnpmWorkspaceYamlOptions;
|
|
174
163
|
|
|
175
164
|
private readonly packages: string[] = [];
|
|
176
165
|
private readonly catalog: Catalog;
|
|
177
166
|
private readonly allowBuilds: AllowBuilds;
|
|
178
167
|
private readonly overrides: Record<string, string>;
|
|
179
168
|
|
|
180
|
-
constructor(
|
|
169
|
+
constructor(
|
|
170
|
+
options: DBXToolsPNPMWorkspaceOptions & Pick<javascript.NodeProjectOptions, "pnpmOptions"> = {},
|
|
171
|
+
) {
|
|
172
|
+
const workspaceOptions = options.pnpmOptions?.workspaceYamlOptions;
|
|
181
173
|
this.catalog = { ...DEFAULT_CATALOG, ...options.catalog };
|
|
182
174
|
this.allowBuilds = { ...DEFAULT_ALLOW_BUILDS, ...options.allowBuilds };
|
|
183
175
|
// Seeded even when empty so `addOverride` has a reference projen already
|
|
184
176
|
// captured; projen's `omitEmpty` drops the key while it stays empty.
|
|
185
|
-
this.overrides = { ...DEFAULT_OVERRIDES, ...
|
|
177
|
+
this.overrides = { ...DEFAULT_OVERRIDES, ...workspaceOptions?.overrides };
|
|
186
178
|
this.options = {
|
|
187
179
|
...DEFAULT_WORKSPACE_YAML,
|
|
188
|
-
...
|
|
180
|
+
...workspaceOptions,
|
|
189
181
|
packages: this.packages,
|
|
190
182
|
catalog: this.catalog,
|
|
191
183
|
allowBuilds: this.allowBuilds,
|
|
@@ -238,8 +230,9 @@ export class PnpmWorkspaceState {
|
|
|
238
230
|
* (e.g. a future projen that DOES create it under bun).
|
|
239
231
|
*/
|
|
240
232
|
public attachWorkspaceFile(project: Project): void {
|
|
241
|
-
|
|
242
|
-
|
|
233
|
+
if (!javascript.PnpmWorkspaceYaml.of(project)) {
|
|
234
|
+
new javascript.PnpmWorkspaceYaml(project, this.options);
|
|
235
|
+
}
|
|
243
236
|
}
|
|
244
237
|
|
|
245
238
|
/**
|
|
@@ -280,7 +273,9 @@ export class PnpmWorkspaceState {
|
|
|
280
273
|
pkg.addField("catalog", { ...this.catalog });
|
|
281
274
|
}
|
|
282
275
|
if (Object.keys(this.overrides).length > 0) {
|
|
283
|
-
pkg.
|
|
276
|
+
pkg.addPackageResolutions(
|
|
277
|
+
...Object.entries(this.overrides).map(([name, version]) => `${name}@${version}`),
|
|
278
|
+
);
|
|
284
279
|
}
|
|
285
280
|
// projen renders bun's `trustedDependencies` from `allowedScripts`.
|
|
286
281
|
const allowed = Object.entries(this.allowBuilds)
|