@dbx-tools/projen 0.6.75 → 0.6.77
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 +38 -0
- package/index.ts +8 -21
- package/package.json +4 -4
- package/src/bun-app.ts +4 -6
- package/src/openapi.ts +87 -20
- package/src/project-js.ts +1237 -0
- package/src/project-predicate.ts +1 -1
- package/src/project-py.ts +329 -0
- package/src/project.ts +46 -1292
- package/src/release.ts +3 -3
- package/src/tags.ts +1 -1
- package/src/tsconfig.ts +2 -2
- package/src/vscode.ts +3 -3
package/README.md
CHANGED
|
@@ -47,6 +47,44 @@ The engine treats generated barrels, tests, declaration files, and folders
|
|
|
47
47
|
without exported source modules as implementation details. They do not create
|
|
48
48
|
new package membership.
|
|
49
49
|
|
|
50
|
+
## Add A Python uv Workspace
|
|
51
|
+
|
|
52
|
+
`DBXToolsPythonWorkspace` attaches Python packaging to an existing projen root
|
|
53
|
+
without turning Python packages into JavaScript workspace projects. It generates
|
|
54
|
+
the root and member `pyproject.toml` files, standard `py:*` tasks, the VS Code
|
|
55
|
+
interpreter setting, and an optional manual PyPI trusted-publishing workflow.
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { project, projectPy } from "@dbx-tools/projen";
|
|
59
|
+
|
|
60
|
+
const root = new project.DBXToolsNodeProject({ name: "my-apps" });
|
|
61
|
+
const repository = {
|
|
62
|
+
url: "https://github.com/example/my-apps.git",
|
|
63
|
+
ref: "main",
|
|
64
|
+
root: "python/packages",
|
|
65
|
+
} as const;
|
|
66
|
+
|
|
67
|
+
new projectPy.DBXToolsPythonWorkspace(root, {
|
|
68
|
+
repository,
|
|
69
|
+
packages: [
|
|
70
|
+
{
|
|
71
|
+
directory: "core",
|
|
72
|
+
name: "my-apps-core",
|
|
73
|
+
module: "my_apps.core",
|
|
74
|
+
description: "Shared Python helpers",
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
interpreterPath: "${workspaceFolder}/python/.venv/bin/python",
|
|
78
|
+
release: true,
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Use `projectPy.pythonGitDependency(repository, name, directory)` for an internal
|
|
83
|
+
dependency that must also resolve when a package is installed directly through a
|
|
84
|
+
Git `#subdirectory=` URL. `root.vscode` is projen's existing VS Code component;
|
|
85
|
+
dbx-tools reuses it rather than constructing a second `.vscode/settings.json`
|
|
86
|
+
owner.
|
|
87
|
+
|
|
50
88
|
## Customize Packages With Mixins
|
|
51
89
|
|
|
52
90
|
```ts
|
package/index.ts
CHANGED
|
@@ -15,7 +15,9 @@ export * as openapi from "./src/openapi.ts";
|
|
|
15
15
|
export * as packages from "./src/packages.ts";
|
|
16
16
|
export * as pnpmWorkspace from "./src/pnpm-workspace.ts";
|
|
17
17
|
export * as project from "./src/project.ts";
|
|
18
|
+
export * as projectJs from "./src/project-js.ts";
|
|
18
19
|
export * as projectPredicate from "./src/project-predicate.ts";
|
|
20
|
+
export * as projectPy from "./src/project-py.ts";
|
|
19
21
|
export * as publish from "./src/publish.ts";
|
|
20
22
|
export * as release from "./src/release.ts";
|
|
21
23
|
export * as scaffold from "./src/scaffold.ts";
|
|
@@ -23,15 +25,7 @@ export * as tags from "./src/tags.ts";
|
|
|
23
25
|
export * as tsconfig from "./src/tsconfig.ts";
|
|
24
26
|
export * as vscode from "./src/vscode.ts";
|
|
25
27
|
export * as watch from "./src/watch.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
|
+
export { BUN_DEV_OVERRIDE, BUN_BUILD_OVERRIDE, BUN_APP_OVERRIDES, RootBunfigFile, BunfigFile, BunDevServerFile, BunBuildFile } from "./src/bun-app.ts";
|
|
35
29
|
export { DBXToolsConfig } from "./src/dbx-tools-config.ts";
|
|
36
30
|
export type { DBXToolsConfigOptions } from "./src/dbx-tools-config.ts";
|
|
37
31
|
export { resolvePkgRoot } from "./src/engine-root.ts";
|
|
@@ -42,18 +36,11 @@ export { repoRoot, DEFAULT_PACKAGE_ROOTS, DiscoveredPackage } from "./src/packag
|
|
|
42
36
|
export type { RecordedPackage } from "./src/packages.ts";
|
|
43
37
|
export { PnpmWorkspaceState } from "./src/pnpm-workspace.ts";
|
|
44
38
|
export type { Catalog, AllowBuilds, DBXToolsPNPMWorkspaceOptions } from "./src/pnpm-workspace.ts";
|
|
45
|
-
export {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
} from "./src/project.ts";
|
|
51
|
-
export type {
|
|
52
|
-
DBXToolsProject,
|
|
53
|
-
DBXToolsProjectOptions,
|
|
54
|
-
DBXToolsTypeScriptProjectOptions,
|
|
55
|
-
ApplyToProjectsOptions,
|
|
56
|
-
} from "./src/project.ts";
|
|
39
|
+
export type { ApplyToProjectsOptions } from "./src/project.ts";
|
|
40
|
+
export { PackageIdentifier, PROJEN_VERSION, DBXToolsNodeProject, ROOT_INSTALL_ONLY_MIXIN, DBXToolsTypeScriptProject } from "./src/project-js.ts";
|
|
41
|
+
export type { DBXToolsProject, DBXToolsProjectOptions, DBXToolsTypeScriptProjectOptions } from "./src/project-js.ts";
|
|
42
|
+
export { DBXToolsPythonWorkspace } from "./src/project-py.ts";
|
|
43
|
+
export type { PythonRepositoryOptions, PythonPackageOptions, PythonReleaseOptions, DBXToolsPythonWorkspaceOptions } from "./src/project-py.ts";
|
|
57
44
|
export { COMPILED_DIR, COMPILED_COMPILER_OPTIONS } from "./src/publish.ts";
|
|
58
45
|
export { DBXToolsRelease } from "./src/release.ts";
|
|
59
46
|
export type { StandaloneRelease, DBXToolsReleaseOptions } from "./src/release.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.77",
|
|
30
|
+
"@dbx-tools/path": "0.6.77",
|
|
31
|
+
"@dbx-tools/shared-core": "0.6.77",
|
|
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.77",
|
|
51
51
|
"types": "index.ts",
|
|
52
52
|
"type": "module",
|
|
53
53
|
"exports": {
|
package/src/bun-app.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Bun browser-app scaffolding as first-class projen file components.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* An `app`-tagged package gets three generated, read-only files that provide its
|
|
5
|
+
* whole dev/build toolchain:
|
|
6
6
|
*
|
|
7
7
|
* - {@link BunfigFile} - `bunfig.toml` wiring `bun-plugin-tailwind` into both the
|
|
8
8
|
* dev server (`[serve.static]`) and `Bun.build` (`[build]`) so Tailwind v4's
|
|
@@ -17,8 +17,7 @@
|
|
|
17
17
|
* Each file supports an unmanaged OVERRIDE beside it (`bunfig.override.toml`,
|
|
18
18
|
* `bun-dev.override.ts`, `bun-build.override.ts`): the dev/build scripts import
|
|
19
19
|
* the override's default export and merge it over the generated options, so a
|
|
20
|
-
* package tweaks its server/bundle WITHOUT editing the projen-owned file
|
|
21
|
-
* same escape hatch the Vite generator offered via `vite.config.override.*`.
|
|
20
|
+
* package tweaks its server/bundle WITHOUT editing the projen-owned file.
|
|
22
21
|
*
|
|
23
22
|
* bun runs these `.ts` files directly. Being package-ROOT files (not under
|
|
24
23
|
* `src/`), they are outside the package's `tsconfig` include, so their `Bun.*`
|
|
@@ -42,8 +41,7 @@ export const BUN_APP_OVERRIDES = ["bunfig.override.toml", BUN_DEV_OVERRIDE, BUN_
|
|
|
42
41
|
* rejects passing a value built against one to an API typed by the other
|
|
43
42
|
* ("separate declarations of a private property"). The hoisted linker de-dupes to
|
|
44
43
|
* a single flat copy (npm-style), which is what keeps identity-based singletons
|
|
45
|
-
* (AppKit `CacheManager`, Mastra classes, one React) coherent
|
|
46
|
-
* the old cross-workspace `.pnpmfile.cjs` bridge used to guarantee.
|
|
44
|
+
* (AppKit `CacheManager`, Mastra classes, one React) coherent.
|
|
47
45
|
*/
|
|
48
46
|
export class RootBunfigFile extends TextFile {
|
|
49
47
|
constructor(project: Project) {
|
package/src/openapi.ts
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* (`from 'tsoa'` / `from '@tsoa/runtime'`) and, for each package that has them,
|
|
6
6
|
* generates a read-only `<root>/openapi/<name>` package:
|
|
7
7
|
*
|
|
8
|
-
* - `openapi.json` - the OpenAPI 3 spec (tsoa `generateSpec`,
|
|
8
|
+
* - `openapi.json` - the OpenAPI 3 spec (tsoa `generateSpec`, then Speakeasy
|
|
9
|
+
* optimization to extract duplicate inline schemas into components).
|
|
9
10
|
* - `src/schema.ts` - types generated from the spec (openapi-typescript).
|
|
10
11
|
* - `src/client.ts` - a typed `openapi-fetch` client, usable client-side.
|
|
11
12
|
*
|
|
@@ -18,11 +19,23 @@
|
|
|
18
19
|
* `tsoa`, `typescript`, and `openapi-typescript` are loaded lazily (heavy, and only
|
|
19
20
|
* needed for `bun run openapi`), so importing this module stays cheap. `tsoa` and
|
|
20
21
|
* `typescript` are not engine dependencies at all - both are resolved out of the
|
|
21
|
-
* consuming workspace, which is where they already live.
|
|
22
|
+
* consuming workspace, which is where they already live. Speakeasy's `openapi`
|
|
23
|
+
* binary is installed lazily through `@dbx-tools/core`'s binary cache.
|
|
22
24
|
*/
|
|
23
|
-
import {
|
|
25
|
+
import { execFile } from "node:child_process";
|
|
26
|
+
import {
|
|
27
|
+
existsSync,
|
|
28
|
+
mkdirSync,
|
|
29
|
+
mkdtempSync,
|
|
30
|
+
readFileSync,
|
|
31
|
+
renameSync,
|
|
32
|
+
rmSync,
|
|
33
|
+
writeFileSync,
|
|
34
|
+
} from "node:fs";
|
|
24
35
|
import { createRequire } from "node:module";
|
|
25
36
|
import { join } from "node:path";
|
|
37
|
+
import { promisify } from "node:util";
|
|
38
|
+
import { bin } from "@dbx-tools/core";
|
|
26
39
|
import { find } from "@dbx-tools/path";
|
|
27
40
|
import { log } from "@dbx-tools/shared-core";
|
|
28
41
|
import type * as ts from "typescript";
|
|
@@ -42,6 +55,9 @@ const logger = log.logger("projen:openapi");
|
|
|
42
55
|
const OPENAPI_TAG = "openapi";
|
|
43
56
|
/** Heuristic: a module file whose source imports tsoa's runtime package. */
|
|
44
57
|
const TSOA_IMPORT = /from\s+['"](?:tsoa|@tsoa\/runtime)['"]/;
|
|
58
|
+
const SPEAKEASY_OPENAPI_VERSION = "1.24.0";
|
|
59
|
+
const SPEAKEASY_OPENAPI_RELEASE_URL = `https://github.com/speakeasy-api/openapi/releases/download/v${SPEAKEASY_OPENAPI_VERSION}`;
|
|
60
|
+
const execFileAsync = promisify(execFile);
|
|
45
61
|
|
|
46
62
|
const CLIENT_SRC = `import createClient, { type ClientOptions } from "openapi-fetch";
|
|
47
63
|
import type { paths } from "./schema";
|
|
@@ -78,6 +94,48 @@ export function isTsoaController(path: string): boolean {
|
|
|
78
94
|
);
|
|
79
95
|
}
|
|
80
96
|
|
|
97
|
+
/** GitHub release asset name for Speakeasy's OpenAPI binary. */
|
|
98
|
+
export function speakeasyOpenapiAssetName(
|
|
99
|
+
platform: NodeJS.Platform = process.platform,
|
|
100
|
+
arch: string = process.arch,
|
|
101
|
+
): string {
|
|
102
|
+
const osName =
|
|
103
|
+
platform === "darwin"
|
|
104
|
+
? "Darwin"
|
|
105
|
+
: platform === "linux"
|
|
106
|
+
? "Linux"
|
|
107
|
+
: platform === "win32"
|
|
108
|
+
? "Windows"
|
|
109
|
+
: undefined;
|
|
110
|
+
const archName = arch === "arm64" ? "arm64" : arch === "x64" ? "x86_64" : undefined;
|
|
111
|
+
if (!osName || !archName) {
|
|
112
|
+
throw new Error(`Speakeasy openapi has no supported release asset for ${platform}/${arch}`);
|
|
113
|
+
}
|
|
114
|
+
const extension = platform === "win32" ? "zip" : "tar.gz";
|
|
115
|
+
return `openapi_${osName}_${archName}.${extension}`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function speakeasyOpenapiPath(): Promise<string> {
|
|
119
|
+
const assetName = speakeasyOpenapiAssetName();
|
|
120
|
+
const context = await bin.ensure("openapi", `${SPEAKEASY_OPENAPI_RELEASE_URL}/${assetName}`, {
|
|
121
|
+
autoUnpackage: true,
|
|
122
|
+
minVersion: SPEAKEASY_OPENAPI_VERSION,
|
|
123
|
+
selector: ({ source }) =>
|
|
124
|
+
join(source, process.platform === "win32" ? "openapi.exe" : "openapi"),
|
|
125
|
+
versionParser: (output) => {
|
|
126
|
+
const version = bin.parseVersion(output);
|
|
127
|
+
return version === SPEAKEASY_OPENAPI_VERSION ? version : undefined;
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
return context.path;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Deduplicate inline schemas into `components.schemas` with Speakeasy. */
|
|
134
|
+
export async function optimizeOpenapiSpec(specPath: string, executable?: string): Promise<void> {
|
|
135
|
+
const openapi = executable ?? (await speakeasyOpenapiPath());
|
|
136
|
+
await execFileAsync(openapi, ["spec", "optimize", specPath, "--write", "--non-interactive"]);
|
|
137
|
+
}
|
|
138
|
+
|
|
81
139
|
/**
|
|
82
140
|
* Regenerate the `openapi` packages from every server/node package with a tsoa
|
|
83
141
|
* import. Returns the package dirs it wrote so the caller can rebuild their barrels.
|
|
@@ -118,28 +176,37 @@ export async function generateOpenapi(): Promise<string[]> {
|
|
|
118
176
|
const written: string[] = [];
|
|
119
177
|
for (const p of pkgs) {
|
|
120
178
|
// The generated package's folder is the source's leaf folder name (`api`), not
|
|
121
|
-
// its npm name - `p.name` is
|
|
179
|
+
// its npm name - `p.name` is the (possibly-overridden) manifest name.
|
|
122
180
|
const leaf = p.relPath.split("/").pop() ?? p.relPath;
|
|
123
181
|
const outDir = join(repoRoot, p.root, OPENAPI_TAG, leaf);
|
|
124
182
|
const srcDir = join(outDir, "src");
|
|
125
183
|
mkdirSync(srcDir, { recursive: true });
|
|
126
184
|
|
|
127
|
-
// 1) tsoa writes
|
|
185
|
+
// 1) tsoa writes a temporary openapi.json, Speakeasy optimizes it there, then
|
|
186
|
+
// the complete spec moves into place so readers never observe an intermediate file.
|
|
128
187
|
const specPath = join(outDir, "openapi.json");
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
188
|
+
const tempDir = mkdtempSync(join(outDir, ".openapi-"));
|
|
189
|
+
const tempSpecPath = join(tempDir, "openapi.json");
|
|
190
|
+
try {
|
|
191
|
+
await generateSpec(
|
|
192
|
+
{
|
|
193
|
+
entryFile: "",
|
|
194
|
+
noImplicitAdditionalProperties: "throw-on-extras",
|
|
195
|
+
controllerPathGlobs: [join(p.dir, "src/**/*.ts")],
|
|
196
|
+
outputDirectory: tempDir,
|
|
197
|
+
specFileBaseName: "openapi",
|
|
198
|
+
specVersion: 3,
|
|
199
|
+
name: `${p.relPath} API`,
|
|
200
|
+
version: "0.0.0",
|
|
201
|
+
},
|
|
202
|
+
compilerOptions,
|
|
203
|
+
);
|
|
204
|
+
await optimizeOpenapiSpec(tempSpecPath);
|
|
205
|
+
makeWritable(specPath);
|
|
206
|
+
renameSync(tempSpecPath, specPath);
|
|
207
|
+
} finally {
|
|
208
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
209
|
+
}
|
|
143
210
|
makeReadonly(specPath);
|
|
144
211
|
|
|
145
212
|
// 2) src/schema.ts: types generated from the spec (openapi-typescript).
|
|
@@ -148,7 +215,7 @@ export async function generateOpenapi(): Promise<string[]> {
|
|
|
148
215
|
makeWritable(schemaPath);
|
|
149
216
|
writeFileSync(schemaPath, astToString(await openapiTS(spec)));
|
|
150
217
|
stampGenerated(schemaPath, {
|
|
151
|
-
tool: "projen openapi (tsoa + openapi-typescript)",
|
|
218
|
+
tool: "projen openapi (tsoa + Speakeasy + openapi-typescript)",
|
|
152
219
|
source: `the tsoa controllers in ${p.relPath}`,
|
|
153
220
|
});
|
|
154
221
|
|