@dbx-tools/projen 0.9.5 → 0.9.7
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 +69 -17
- package/index.ts +7 -5
- package/package.json +9 -6
- package/src/_rust-project.ts +356 -0
- package/src/_rust-release-workflow.ts +954 -0
- package/src/_rust-release.ts +605 -0
- package/src/_task-command.ts +89 -0
- package/src/barrels.ts +12 -11
- package/src/bun-app.ts +96 -85
- package/src/bun-workflow.ts +60 -52
- package/src/engine-root.ts +5 -5
- package/src/openapi.ts +16 -9
- package/src/packages.ts +9 -9
- package/src/pnpm-workspace.ts +1 -1
- package/src/project-js.ts +19 -25
- package/src/project-py.ts +28 -23
- package/src/project-rs.ts +100 -1098
- package/src/projen-version.ts +9 -0
- package/src/publish.ts +3 -4
- package/src/release-catalog.ts +1 -1
- package/src/release.ts +212 -177
- package/src/scaffold.ts +1 -1
- package/src/uniffi.ts +7 -0
- package/src/watch.ts +2 -2
- package/src/workspace-version.ts +20 -15
- package/tasks/barrels.ts +4 -4
- package/tasks/build-rust-release.ts +68 -0
- package/tasks/bump.ts +3 -2
- package/tasks/clean.ts +3 -3
- package/tasks/local-publish.ts +21 -3
- package/tasks/openapi.ts +2 -2
- package/tasks/publish-npm.ts +12 -6
- package/tasks/publish-python.ts +2 -2
- package/tasks/publish-uniffi-local.ts +11 -15
- package/tasks/publish.ts +88 -88
- package/tasks/python-registry.ts +1 -1
- package/tasks/release-bootstrap.ts +2 -2
- package/tasks/release-plan.ts +1 -1
- package/tasks/release-please.ts +1 -1
- package/tasks/release-pr.ts +60 -115
- package/tasks/release-request.ts +8 -29
- package/tasks/release-summary-units.ts +2 -9
- package/tasks/release-summary.ts +35 -24
- package/tasks/release-version.ts +44 -0
- package/tasks/rust-release.d.ts +41 -0
- package/tasks/rust-release.mjs +1717 -0
- package/tasks/sync.ts +3 -3
- package/tasks/uniffi-python.js +1 -1
- package/tasks/uniffi.ts +7 -1
- package/tasks/version-check.ts +6 -5
package/src/barrels.ts
CHANGED
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
60
60
|
import { join, relative } from "node:path";
|
|
61
61
|
import { find } from "@dbx-tools/path";
|
|
62
|
-
import { json,
|
|
62
|
+
import { json, stringUtils } from "@dbx-tools/shared-core";
|
|
63
63
|
import isIdentifier from "is-identifier";
|
|
64
64
|
import { header, isGenerated, makeReadonly, makeWritable, type HeaderOpts } from "./generated.ts";
|
|
65
65
|
import { moduleExports, moduleStatements, type ModuleExport } from "./module-exports.ts";
|
|
@@ -125,9 +125,11 @@ const UNIFFI_BINDINGS_FILE = "bindings.ts";
|
|
|
125
125
|
const UNIFFI_GENERATED_FILE = "_bindings.ts";
|
|
126
126
|
const UNIFFI_FFI_FILE = "_bindings-ffi.ts";
|
|
127
127
|
|
|
128
|
-
/** `
|
|
129
|
-
function
|
|
130
|
-
const tokens = [
|
|
128
|
+
/** `config-tools` / `config_tools` -> `configTools`; `local-fs` -> `localFS`. */
|
|
129
|
+
function moduleSegmentToCamel(segment: string): string {
|
|
130
|
+
const tokens = [
|
|
131
|
+
...stringUtils.tokenizeWithOptions({ lowerCase: true, capitalize: true }, segment),
|
|
132
|
+
];
|
|
131
133
|
if (tokens.length === 0) return segment;
|
|
132
134
|
const [first, ...rest] = tokens;
|
|
133
135
|
// Standalone acronym modules (`fs`, `ai`) stay lowercase so they match
|
|
@@ -145,14 +147,14 @@ function modulePathToNamespace(modulePath: string): string {
|
|
|
145
147
|
const rel = modulePath.replace(/^\.\/src\//, "").replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
|
|
146
148
|
const segments = rel.split("/");
|
|
147
149
|
if (segments.at(-1) === "index" && segments.length > 1) segments.pop();
|
|
148
|
-
const names = segments.map(
|
|
150
|
+
const names = segments.map(moduleSegmentToCamel);
|
|
149
151
|
let name =
|
|
150
152
|
names.length === 1
|
|
151
153
|
? names[0]!
|
|
152
154
|
: names[0]! +
|
|
153
155
|
names
|
|
154
156
|
.slice(1)
|
|
155
|
-
.map((s) =>
|
|
157
|
+
.map((s) => stringUtils.capitalize(s))
|
|
156
158
|
.join("");
|
|
157
159
|
if (!isIdentifier(name)) {
|
|
158
160
|
name = `${name}Module`;
|
|
@@ -521,10 +523,9 @@ const WRITE_ATTEMPTS = 3;
|
|
|
521
523
|
* and the projenrc watcher's post-synth `generateBarrels()` sweep), so any gap
|
|
522
524
|
* between `makeWritable` and the write is a window in which another process's
|
|
523
525
|
* `makeReadonly` lands and this write fails with
|
|
524
|
-
* `EACCES: permission denied, open '<pkg>/index.ts'`.
|
|
525
|
-
* the
|
|
526
|
-
*
|
|
527
|
-
* and a retry absorbs what is left.
|
|
526
|
+
* `EACCES: permission denied, open '<pkg>/index.ts'`. Keeping the unlock adjacent
|
|
527
|
+
* to the write avoids a gap during export parsing, and a retry absorbs what is
|
|
528
|
+
* left.
|
|
528
529
|
*
|
|
529
530
|
* Do NOT "simplify" this back to a single unlock-then-write: the failure is
|
|
530
531
|
* timing-dependent, so it looks fine until a full-repo sweep runs against a
|
|
@@ -576,7 +577,7 @@ export function generateBarrels(
|
|
|
576
577
|
const names = failures.map((f) => relative(projectRoot, f.dir) || f.dir);
|
|
577
578
|
throw new AggregateError(
|
|
578
579
|
failures.map((f) => f.err),
|
|
579
|
-
`${
|
|
580
|
+
`${stringUtils.pluralize(failures.length, "barrel")} failed: ${names.join(", ")}`,
|
|
580
581
|
);
|
|
581
582
|
}
|
|
582
583
|
return total;
|
package/src/bun-app.ts
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* `src/`), they are outside the package's `tsconfig` include, so their `Bun.*`
|
|
24
24
|
* usage never trips the `app` package's DOM-only `compile`.
|
|
25
25
|
*/
|
|
26
|
+
import { stringUtils } from "@dbx-tools/shared-core";
|
|
26
27
|
import { type Project, TextFile } from "projen";
|
|
27
28
|
|
|
28
29
|
/** Unmanaged dev-server override module, merged over the generated options if present. */
|
|
@@ -90,34 +91,38 @@ export class BunfigFile extends TextFile {
|
|
|
90
91
|
/** `dev.ts`: a `Bun.serve()` fullstack dev server with React HMR. */
|
|
91
92
|
export class BunDevServerFile extends TextFile {
|
|
92
93
|
constructor(project: Project) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
import { existsSync } from "node:fs";
|
|
98
|
-
import index from "./index.html";
|
|
94
|
+
// prettier-ignore
|
|
95
|
+
const source = (
|
|
96
|
+
// ============================================================================
|
|
97
|
+
/*ts*/`
|
|
98
|
+
import { existsSync } from "node:fs";
|
|
99
|
+
import index from "./index.html";
|
|
99
100
|
|
|
100
|
-
// Unmanaged override (${BUN_DEV_OVERRIDE}): its default export is merged over the
|
|
101
|
-
// generated serve options - later wins - so a package tweaks the dev server
|
|
102
|
-
// without editing this generated file.
|
|
103
|
-
let options: Bun.ServeFunctionOptions<unknown, {}> = {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
};
|
|
101
|
+
// Unmanaged override (${BUN_DEV_OVERRIDE}): its default export is merged over the
|
|
102
|
+
// generated serve options - later wins - so a package tweaks the dev server
|
|
103
|
+
// without editing this generated file.
|
|
104
|
+
let options: Bun.ServeFunctionOptions<unknown, {}> = {
|
|
105
|
+
port: Number(process.env.PORT ?? 5173),
|
|
106
|
+
development: { hmr: true, console: true },
|
|
107
|
+
routes: { "/*": index },
|
|
108
|
+
};
|
|
108
109
|
|
|
109
|
-
const overrideUrl = new URL("./${BUN_DEV_OVERRIDE}", import.meta.url);
|
|
110
|
-
if (existsSync(overrideUrl)) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
110
|
+
const overrideUrl = new URL("./${BUN_DEV_OVERRIDE}", import.meta.url);
|
|
111
|
+
if (existsSync(overrideUrl)) {
|
|
112
|
+
const mod = await import(overrideUrl.href);
|
|
113
|
+
const override = typeof mod.default === "function" ? await mod.default() : mod.default;
|
|
114
|
+
options = { ...options, ...override };
|
|
115
|
+
}
|
|
115
116
|
|
|
116
|
-
const server = Bun.serve(options);
|
|
117
|
-
console.log("dev server running at " + server.url.href);
|
|
118
|
-
`
|
|
119
|
-
|
|
120
|
-
|
|
117
|
+
const server = Bun.serve(options);
|
|
118
|
+
console.log("dev server running at " + server.url.href);
|
|
119
|
+
`
|
|
120
|
+
// ============================================================================
|
|
121
|
+
);
|
|
122
|
+
super(project, "dev.ts", {
|
|
123
|
+
marker: true,
|
|
124
|
+
readonly: true,
|
|
125
|
+
lines: stringUtils.dedentLines(source, { trimEnd: false }),
|
|
121
126
|
});
|
|
122
127
|
}
|
|
123
128
|
}
|
|
@@ -125,73 +130,79 @@ console.log("dev server running at " + server.url.href);
|
|
|
125
130
|
/** `build.ts`: a `Bun.build()` production bundle (Tailwind via the plugin API). */
|
|
126
131
|
export class BunBuildFile extends TextFile {
|
|
127
132
|
constructor(project: Project) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
import {
|
|
135
|
-
import
|
|
133
|
+
// Raw template preserves backslashes in generated regular expression literals.
|
|
134
|
+
// prettier-ignore
|
|
135
|
+
const source = (
|
|
136
|
+
String.raw
|
|
137
|
+
// ============================================================================
|
|
138
|
+
/*ts*/`
|
|
139
|
+
import { existsSync } from "node:fs";
|
|
140
|
+
import { cp, rm } from "node:fs/promises";
|
|
141
|
+
import { basename } from "node:path";
|
|
142
|
+
import tailwind from "bun-plugin-tailwind";
|
|
136
143
|
|
|
137
|
-
// Unmanaged override (${BUN_BUILD_OVERRIDE}): its default export is merged over
|
|
138
|
-
// the generated build options - later wins.
|
|
139
|
-
let options: Bun.BuildConfig = {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
};
|
|
144
|
+
// Unmanaged override (${BUN_BUILD_OVERRIDE}): its default export is merged over
|
|
145
|
+
// the generated build options - later wins.
|
|
146
|
+
let options: Bun.BuildConfig = {
|
|
147
|
+
entrypoints: ["./index.html"],
|
|
148
|
+
outdir: "./dist",
|
|
149
|
+
define: { "process.env.NODE_ENV": JSON.stringify("production") },
|
|
150
|
+
minify: true,
|
|
151
|
+
splitting: true,
|
|
152
|
+
publicPath: "/",
|
|
153
|
+
external: ["/fonts/*"],
|
|
154
|
+
sourcemap: "none",
|
|
155
|
+
plugins: [tailwind],
|
|
156
|
+
};
|
|
150
157
|
|
|
151
|
-
const overrideUrl = new URL("./${BUN_BUILD_OVERRIDE}", import.meta.url);
|
|
152
|
-
if (existsSync(overrideUrl)) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
158
|
+
const overrideUrl = new URL("./${BUN_BUILD_OVERRIDE}", import.meta.url);
|
|
159
|
+
if (existsSync(overrideUrl)) {
|
|
160
|
+
const mod = await import(overrideUrl.href);
|
|
161
|
+
const override = typeof mod.default === "function" ? await mod.default() : mod.default;
|
|
162
|
+
options = { ...options, ...override, plugins: [...(options.plugins ?? []), ...(override.plugins ?? [])] };
|
|
163
|
+
}
|
|
157
164
|
|
|
158
|
-
const outdir = options.outdir ?? "./dist";
|
|
159
|
-
await rm(outdir, { recursive: true, force: true });
|
|
165
|
+
const outdir = options.outdir ?? "./dist";
|
|
166
|
+
await rm(outdir, { recursive: true, force: true });
|
|
160
167
|
|
|
161
|
-
const result = await Bun.build(options);
|
|
162
|
-
if (!result.success) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
168
|
+
const result = await Bun.build(options);
|
|
169
|
+
if (!result.success) {
|
|
170
|
+
for (const message of result.logs) console.error(message);
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
166
173
|
|
|
167
|
-
// Fix a Bun bug: with \`splitting: true\` and an HTML entrypoint, the emitted
|
|
168
|
-
// HTML's <script src> is wired to an arbitrary chunk instead of the JS
|
|
169
|
-
// entry-point, so the app never boots (a blank page). Rewrite the HTML script
|
|
170
|
-
// (and stylesheet) to the real entry-point outputs. No-op when they already
|
|
171
|
-
// match (e.g. splitting off), so it is always safe to run.
|
|
172
|
-
const htmlOut = result.outputs.find((o) => o.path.endsWith(".html"));
|
|
173
|
-
const entryJs = result.outputs.find((o) => o.kind === "entry-point" && o.path.endsWith(".js"));
|
|
174
|
-
const entryCss = result.outputs.find((o) => o.kind === "entry-point" && o.path.endsWith(".css"));
|
|
175
|
-
if (htmlOut && entryJs) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
174
|
+
// Fix a Bun bug: with \`splitting: true\` and an HTML entrypoint, the emitted
|
|
175
|
+
// HTML's <script src> is wired to an arbitrary chunk instead of the JS
|
|
176
|
+
// entry-point, so the app never boots (a blank page). Rewrite the HTML script
|
|
177
|
+
// (and stylesheet) to the real entry-point outputs. No-op when they already
|
|
178
|
+
// match (e.g. splitting off), so it is always safe to run.
|
|
179
|
+
const htmlOut = result.outputs.find((o) => o.path.endsWith(".html"));
|
|
180
|
+
const entryJs = result.outputs.find((o) => o.kind === "entry-point" && o.path.endsWith(".js"));
|
|
181
|
+
const entryCss = result.outputs.find((o) => o.kind === "entry-point" && o.path.endsWith(".css"));
|
|
182
|
+
if (htmlOut && entryJs) {
|
|
183
|
+
let html = await Bun.file(htmlOut.path).text();
|
|
184
|
+
const jsName = basename(entryJs.path);
|
|
185
|
+
html = html.replace(/(<script[^>]*\bsrc=")([^"]*\/)?[^"/]+\.js(")/i, "$1$2" + jsName + "$3");
|
|
186
|
+
if (entryCss) {
|
|
187
|
+
const cssName = basename(entryCss.path);
|
|
188
|
+
html = html.replace(/(<link[^>]*\bhref=")([^"]*\/)?[^"/]+\.css(")/i, "$1$2" + cssName + "$3");
|
|
189
|
+
}
|
|
190
|
+
await Bun.write(htmlOut.path, html);
|
|
191
|
+
}
|
|
185
192
|
|
|
186
|
-
const publicDir = "./public";
|
|
187
|
-
if (existsSync(publicDir)) {
|
|
188
|
-
|
|
189
|
-
}
|
|
193
|
+
const publicDir = "./public";
|
|
194
|
+
if (existsSync(publicDir)) {
|
|
195
|
+
await cp(publicDir, outdir, { recursive: true });
|
|
196
|
+
}
|
|
190
197
|
|
|
191
|
-
console.log("built " + result.outputs.length + " files to " + options.outdir);
|
|
192
|
-
`
|
|
193
|
-
|
|
194
|
-
|
|
198
|
+
console.log("built " + result.outputs.length + " files to " + options.outdir);
|
|
199
|
+
`
|
|
200
|
+
// ============================================================================
|
|
201
|
+
);
|
|
202
|
+
super(project, "build.ts", {
|
|
203
|
+
marker: true,
|
|
204
|
+
readonly: true,
|
|
205
|
+
lines: stringUtils.dedentLines(source, { trimEnd: false }),
|
|
195
206
|
});
|
|
196
207
|
}
|
|
197
208
|
}
|
package/src/bun-workflow.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/** Shared Bun setup and package-cache steps for generated workflows. */
|
|
2
|
+
import { stringUtils } from "@dbx-tools/shared-core";
|
|
2
3
|
import { TextFile, javascript } from "projen";
|
|
3
4
|
|
|
4
5
|
export const BUN_VERSION = "1.3.14";
|
|
@@ -16,61 +17,68 @@ const DEFAULT_CACHE_IGNORE_PATHS = [
|
|
|
16
17
|
|
|
17
18
|
function cacheKeyScript(extraIgnorePaths: readonly string[]): string {
|
|
18
19
|
const ignorePaths = [...new Set(extraIgnorePaths)].sort();
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
// prettier-ignore
|
|
21
|
+
const source = (
|
|
22
|
+
// ============================================================================
|
|
23
|
+
/*js*/`
|
|
24
|
+
#!/usr/bin/env node
|
|
25
|
+
import { createHash } from "node:crypto";
|
|
26
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
27
|
+
import { join, sep } from "node:path";
|
|
23
28
|
|
|
24
|
-
const root = process.cwd();
|
|
25
|
-
const ignoredNames = new Set(${JSON.stringify(DEFAULT_CACHE_IGNORE_PATHS)});
|
|
26
|
-
const ignoredPaths = new Set(${JSON.stringify(ignorePaths)});
|
|
27
|
-
const manifests = [];
|
|
28
|
-
const walk = (directory) => {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
};
|
|
37
|
-
walk(root);
|
|
29
|
+
const root = process.cwd();
|
|
30
|
+
const ignoredNames = new Set(${JSON.stringify(DEFAULT_CACHE_IGNORE_PATHS)});
|
|
31
|
+
const ignoredPaths = new Set(${JSON.stringify(ignorePaths)});
|
|
32
|
+
const manifests = [];
|
|
33
|
+
const walk = (directory) => {
|
|
34
|
+
for (const entry of readdirSync(directory, { withFileTypes: true })) {
|
|
35
|
+
const path = join(directory, entry.name);
|
|
36
|
+
const relativePath = path.slice(root.length + 1).split(sep).join("/");
|
|
37
|
+
if (ignoredNames.has(entry.name) || ignoredPaths.has(relativePath)) continue;
|
|
38
|
+
if (entry.isDirectory()) walk(path);
|
|
39
|
+
else if (entry.name === "package.json") manifests.push(path);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
walk(root);
|
|
38
43
|
|
|
39
|
-
const dependencyFields = [
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
];
|
|
50
|
-
const canonical = (value) => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
44
|
+
const dependencyFields = [
|
|
45
|
+
"catalog",
|
|
46
|
+
"dependencies",
|
|
47
|
+
"devDependencies",
|
|
48
|
+
"optionalDependencies",
|
|
49
|
+
"overrides",
|
|
50
|
+
"peerDependencies",
|
|
51
|
+
"peerDependenciesMeta",
|
|
52
|
+
"resolutions",
|
|
53
|
+
"trustedDependencies",
|
|
54
|
+
];
|
|
55
|
+
const canonical = (value) => {
|
|
56
|
+
if (Array.isArray(value)) return value.map(canonical);
|
|
57
|
+
if (!value || typeof value !== "object") return value;
|
|
58
|
+
return Object.fromEntries(
|
|
59
|
+
Object.entries(value)
|
|
60
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
61
|
+
.map(([key, child]) => [key, canonical(child)]),
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
const dependencies = manifests
|
|
65
|
+
.sort()
|
|
66
|
+
.map((path) => {
|
|
67
|
+
const manifest = JSON.parse(readFileSync(path, "utf8"));
|
|
68
|
+
return [
|
|
69
|
+
path.slice(root.length + 1),
|
|
70
|
+
Object.fromEntries(
|
|
71
|
+
dependencyFields
|
|
72
|
+
.filter((field) => manifest[field] !== undefined)
|
|
73
|
+
.map((field) => [field, canonical(manifest[field])]),
|
|
74
|
+
),
|
|
75
|
+
];
|
|
76
|
+
});
|
|
77
|
+
process.stdout.write(createHash("sha256").update(JSON.stringify(dependencies)).digest("hex"));
|
|
78
|
+
`
|
|
79
|
+
// ============================================================================
|
|
57
80
|
);
|
|
58
|
-
};
|
|
59
|
-
const dependencies = manifests
|
|
60
|
-
.sort()
|
|
61
|
-
.map((path) => {
|
|
62
|
-
const manifest = JSON.parse(readFileSync(path, "utf8"));
|
|
63
|
-
return [
|
|
64
|
-
path.slice(root.length + 1),
|
|
65
|
-
Object.fromEntries(
|
|
66
|
-
dependencyFields
|
|
67
|
-
.filter((field) => manifest[field] !== undefined)
|
|
68
|
-
.map((field) => [field, canonical(manifest[field])]),
|
|
69
|
-
),
|
|
70
|
-
];
|
|
71
|
-
});
|
|
72
|
-
process.stdout.write(createHash("sha256").update(JSON.stringify(dependencies)).digest("hex"));
|
|
73
|
-
`;
|
|
81
|
+
return stringUtils.dedent(source, { trimEnd: false });
|
|
74
82
|
}
|
|
75
83
|
|
|
76
84
|
const configured = new WeakMap<javascript.NodeProject, string>();
|
package/src/engine-root.ts
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { dirname } from "node:path";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
9
|
+
import * as projectUtils from "@dbx-tools/core/project-utils";
|
|
10
|
+
import { functionUtils } from "@dbx-tools/shared-core";
|
|
11
11
|
|
|
12
12
|
const ENGINE_PKG = "@dbx-tools/projen";
|
|
13
13
|
|
|
@@ -17,11 +17,11 @@ const ENGINE_PKG = "@dbx-tools/projen";
|
|
|
17
17
|
* Walks up from this module with shared-core's {@link project.root} (the nearest
|
|
18
18
|
* package bounded by the enclosing npm/git root), so it resolves both in-repo and
|
|
19
19
|
* when installed as a dependency. Memoized with shared-core's
|
|
20
|
-
* {@link
|
|
20
|
+
* {@link functionUtils.memoize}, which caches only a successful result - a
|
|
21
21
|
* throw is retried on the next call.
|
|
22
22
|
*/
|
|
23
|
-
export const resolvePkgRoot =
|
|
24
|
-
const found =
|
|
23
|
+
export const resolvePkgRoot = functionUtils.memoize((): string => {
|
|
24
|
+
const found = projectUtils.root(dirname(fileURLToPath(import.meta.url)));
|
|
25
25
|
if (!found) throw new Error(`${ENGINE_PKG} package root not found`);
|
|
26
26
|
return found;
|
|
27
27
|
});
|
package/src/openapi.ts
CHANGED
|
@@ -35,9 +35,9 @@ import {
|
|
|
35
35
|
import { createRequire } from "node:module";
|
|
36
36
|
import { join } from "node:path";
|
|
37
37
|
import { promisify } from "node:util";
|
|
38
|
-
import
|
|
38
|
+
import * as bin from "@dbx-tools/core/bin";
|
|
39
39
|
import { find } from "@dbx-tools/path";
|
|
40
|
-
import { log } from "@dbx-tools/shared-core";
|
|
40
|
+
import { log, stringUtils } from "@dbx-tools/shared-core";
|
|
41
41
|
import type * as ts from "typescript";
|
|
42
42
|
import { lazyRequire } from "./_lazy-require.ts";
|
|
43
43
|
import { makeReadonly, makeWritable, stampGenerated } from "./generated.ts";
|
|
@@ -61,14 +61,21 @@ const SPEAKEASY_OPENAPI_VERSION = "1.24.0";
|
|
|
61
61
|
const SPEAKEASY_OPENAPI_RELEASE_URL = `https://github.com/speakeasy-api/openapi/releases/download/v${SPEAKEASY_OPENAPI_VERSION}`;
|
|
62
62
|
const execFileAsync = promisify(execFile);
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
// prettier-ignore
|
|
65
|
+
const CLIENT_TEMPLATE = (
|
|
66
|
+
// ============================================================================
|
|
67
|
+
/*ts*/`
|
|
68
|
+
import createClient, { type ClientOptions } from "openapi-fetch";
|
|
69
|
+
import type { paths } from "./schema";
|
|
66
70
|
|
|
67
|
-
/** Create a typed OpenAPI client (openapi-fetch); safe to use in the browser. */
|
|
68
|
-
export function createApiClient(options?: ClientOptions) {
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
71
|
+
/** Create a typed OpenAPI client (openapi-fetch); safe to use in the browser. */
|
|
72
|
+
export function createApiClient(options?: ClientOptions) {
|
|
73
|
+
return createClient<paths>(options);
|
|
74
|
+
}
|
|
75
|
+
`
|
|
76
|
+
// ============================================================================
|
|
77
|
+
);
|
|
78
|
+
const CLIENT_SRC = stringUtils.dedent(CLIENT_TEMPLATE, { trimEnd: false });
|
|
72
79
|
|
|
73
80
|
/** True if any module file in `<pkg>/src` matches {@link TSOA_IMPORT}. */
|
|
74
81
|
function hasTsoaControllers(pkg: Pick<RecordedPackage, "dir">): boolean {
|
package/src/packages.ts
CHANGED
|
@@ -25,14 +25,14 @@
|
|
|
25
25
|
*/
|
|
26
26
|
import { existsSync, readFileSync } from "node:fs";
|
|
27
27
|
import { extname, relative, resolve, sep } from "node:path";
|
|
28
|
-
import
|
|
28
|
+
import * as projectUtils from "@dbx-tools/core/project-utils";
|
|
29
29
|
import { find } from "@dbx-tools/path";
|
|
30
|
-
import { json, object,
|
|
30
|
+
import { json, object, stringUtils } from "@dbx-tools/shared-core";
|
|
31
31
|
import { parse } from "yaml";
|
|
32
32
|
|
|
33
33
|
/** Resolve the nearest package/projenrc root at call time. */
|
|
34
34
|
export function resolveRepoRoot(cwd: string = process.cwd()): string {
|
|
35
|
-
return
|
|
35
|
+
return projectUtils.root(cwd) ?? cwd;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/** Current-process repository root for executable task entrypoints. */
|
|
@@ -47,11 +47,11 @@ export const DEFAULT_PACKAGE_ROOTS = ["packages"] as const;
|
|
|
47
47
|
/**
|
|
48
48
|
* A project name: the root `package.json` name, else the git remote's repo
|
|
49
49
|
* name, else the root folder name. Delegates to `@dbx-tools/core`'s
|
|
50
|
-
* {@link
|
|
50
|
+
* {@link projectUtils.name}, which is also what a consuming repo's own tooling
|
|
51
51
|
* sees, so the engine and its host agree on the name.
|
|
52
52
|
*/
|
|
53
53
|
export function projectName(projectRoot: string = resolveRepoRoot()): string {
|
|
54
|
-
return
|
|
54
|
+
return projectUtils.name(projectRoot);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
const MODULE_EXTS = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
@@ -65,7 +65,7 @@ export function toPosix(p: string): string {
|
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
67
|
* Cumulative nesting tags from a package's path segments relative to its discovery
|
|
68
|
-
* root. Each segment is kebab-cased with {@link
|
|
68
|
+
* root. Each segment is kebab-cased with {@link stringUtils.toSlug} (`coolDude` ->
|
|
69
69
|
* `cool-dude`). The leaf folder (the package name) is excluded when there are two
|
|
70
70
|
* or more segments; a lone segment tags itself.
|
|
71
71
|
*/
|
|
@@ -75,7 +75,7 @@ function nestingTagsFromSegments(segments: readonly string[]): string[] {
|
|
|
75
75
|
const out: string[] = [];
|
|
76
76
|
let acc = "";
|
|
77
77
|
for (const segment of prefix) {
|
|
78
|
-
const token =
|
|
78
|
+
const token = stringUtils.toSlug(segment);
|
|
79
79
|
if (!token) continue;
|
|
80
80
|
acc = acc ? `${acc}-${token}` : token;
|
|
81
81
|
out.push(acc);
|
|
@@ -288,7 +288,7 @@ function readManifestTags(dir: string): string[] | undefined {
|
|
|
288
288
|
*/
|
|
289
289
|
export function syncResynthPaths(projectRoot: string = resolveRepoRoot()): string[] {
|
|
290
290
|
const paths = readDbxToolsConfig(projectRoot)?.syncResynthPaths;
|
|
291
|
-
return Array.isArray(paths) ?
|
|
291
|
+
return Array.isArray(paths) ? stringUtils.parseList(paths.map((p) => String(p))) : [];
|
|
292
292
|
}
|
|
293
293
|
|
|
294
294
|
/**
|
|
@@ -322,7 +322,7 @@ export function recordedPackages(projectRoot: string = resolveRepoRoot()): Recor
|
|
|
322
322
|
export function recordedRoots(projectRoot: string = resolveRepoRoot()): string[] {
|
|
323
323
|
const configured = readDbxToolsConfig(projectRoot)?.packageRoots;
|
|
324
324
|
if (Array.isArray(configured)) {
|
|
325
|
-
const roots =
|
|
325
|
+
const roots = stringUtils.parseList(configured.map((root) => String(root)));
|
|
326
326
|
if (roots.length) return roots;
|
|
327
327
|
}
|
|
328
328
|
const roots = new Set<string>(DEFAULT_PACKAGE_ROOTS);
|
package/src/pnpm-workspace.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Inputs for `pnpm-workspace.yaml`, which projen's NATIVE
|
|
3
3
|
* `javascript.PnpmWorkspaceYaml` owns.
|
|
4
4
|
*
|
|
5
|
-
* projen
|
|
5
|
+
* projen writes this file itself: every pnpm `NodeProject` gets a
|
|
6
6
|
* `PnpmWorkspaceYaml` component typed by `PnpmWorkspaceYamlSchema`, fed from
|
|
7
7
|
* `pnpmOptions.workspaceYamlOptions`. Nothing here writes a file; this only
|
|
8
8
|
* supplies the options object it renders, so the whole pnpm schema (`overrides`,
|