@dbx-tools/projen 0.6.142 → 0.6.144
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 +37 -0
- package/index.ts +6 -0
- package/package.json +4 -4
- package/src/barrels.ts +33 -16
- package/src/packages.ts +1 -1
- package/src/project-predicate.ts +2 -1
- package/src/project-py.ts +19 -4
- package/src/project-rs.ts +644 -0
- package/src/project.ts +2 -1
- package/src/uniffi.ts +50 -0
- package/tasks/barrels.ts +14 -2
- package/tasks/bump.ts +69 -1
- package/tasks/publish-python.ts +8 -2
- package/tasks/publish-uniffi-local.ts +207 -0
- package/tasks/rust.ts +132 -0
- package/tasks/sync.ts +31 -15
- package/tasks/uniffi-release.ts +168 -0
- package/tasks/uniffi.ts +224 -0
package/README.md
CHANGED
|
@@ -88,6 +88,43 @@ Git `#subdirectory=` URL. `root.vscode` is projen's existing VS Code component;
|
|
|
88
88
|
dbx-tools reuses it rather than constructing a second `.vscode/settings.json`
|
|
89
89
|
owner.
|
|
90
90
|
|
|
91
|
+
## Add A Rust Workspace
|
|
92
|
+
|
|
93
|
+
`DBXToolsRustWorkspace` discovers every source-bearing folder under
|
|
94
|
+
`packages/rs`, generates its Cargo manifest, and derives the crate name from the
|
|
95
|
+
root scope plus folder name. A crate containing `uniffi::setup_scaffolding!()`
|
|
96
|
+
automatically wires matching private Node and Python binding packages using the
|
|
97
|
+
same capability name. Repository-specific dependencies and features remain
|
|
98
|
+
declarative options in `.projenrc.ts`; generated bindings are built separately
|
|
99
|
+
from projen synthesis.
|
|
100
|
+
|
|
101
|
+
`sync --watch` runs a focused Rust watcher beside the OpenAPI watcher. Changes
|
|
102
|
+
inside an existing UniFFI crate regenerate only that crate's bindings; adding or
|
|
103
|
+
removing a crate or `setup_scaffolding!()` marker triggers a full synth. Repos
|
|
104
|
+
without Rust crates start no Rust watcher. When Rust projects are detected,
|
|
105
|
+
Cargo is required and the focused task fails immediately if it is unavailable.
|
|
106
|
+
|
|
107
|
+
The workspace also generates a `rust-release` workflow from those discovered
|
|
108
|
+
UniFFI crates. It builds Linux x64/arm64, macOS x64/arm64, and Windows x64.
|
|
109
|
+
Node publishes a small facade plus optional OS/CPU packages, so npm installs
|
|
110
|
+
only the matching native library. Python publishes one platform-tagged wheel
|
|
111
|
+
per target, so pip follows the same thin-install model.
|
|
112
|
+
|
|
113
|
+
Set the repository variable `LOCAL_REPOSITORIES=true` to enable the generated
|
|
114
|
+
self-hosted mirror job. Configure `LOCAL_NPM_REGISTRY` and
|
|
115
|
+
`LOCAL_PYPI_PUBLISH_URL` with their matching `LOCAL_*` credentials. Set
|
|
116
|
+
`LOCAL_CARGO_REGISTRY` to a named Cargo registry such as a loopback
|
|
117
|
+
[Kellnr](https://kellnr.io/) instance and provide `LOCAL_CARGO_TOKEN`. The runner
|
|
118
|
+
detects its OS, architecture, libc, Rust target, and Python wheel tag and builds
|
|
119
|
+
only that native target. Public Cargo crates also publish directly to crates.io
|
|
120
|
+
with `CARGO_REGISTRY_TOKEN`. Override `releaseTargets` only when a consumer has
|
|
121
|
+
additional native runners; ordinary projects inherit the maintained matrix
|
|
122
|
+
automatically.
|
|
123
|
+
|
|
124
|
+
Private Python projects are marked with `[tool.dbx-tools] private = true`. They
|
|
125
|
+
are excluded from the uv workspace, documentation, and Python release workflow
|
|
126
|
+
until their native artifact publishing matrix is enabled.
|
|
127
|
+
|
|
91
128
|
## Customize Packages With Mixins
|
|
92
129
|
|
|
93
130
|
```ts
|
package/index.ts
CHANGED
|
@@ -19,11 +19,13 @@ export * as project from "./src/project.ts";
|
|
|
19
19
|
export * as projectJs from "./src/project-js.ts";
|
|
20
20
|
export * as projectPredicate from "./src/project-predicate.ts";
|
|
21
21
|
export * as projectPy from "./src/project-py.ts";
|
|
22
|
+
export * as projectRs from "./src/project-rs.ts";
|
|
22
23
|
export * as publish from "./src/publish.ts";
|
|
23
24
|
export * as release from "./src/release.ts";
|
|
24
25
|
export * as scaffold from "./src/scaffold.ts";
|
|
25
26
|
export * as tags from "./src/tags.ts";
|
|
26
27
|
export * as tsconfig from "./src/tsconfig.ts";
|
|
28
|
+
export * as uniffi from "./src/uniffi.ts";
|
|
27
29
|
export * as vscode from "./src/vscode.ts";
|
|
28
30
|
export * as watch from "./src/watch.ts";
|
|
29
31
|
export * as workspaceVersion from "./src/workspace-version.ts";
|
|
@@ -43,12 +45,16 @@ export { PackageIdentifier, PROJEN_VERSION, DBXToolsNodeProject, ROOT_INSTALL_ON
|
|
|
43
45
|
export type { DBXToolsJavaScriptProject, DBXToolsJavaScriptProjectOptions, DBXToolsTypeScriptProjectOptions } from "./src/project-js.ts";
|
|
44
46
|
export { DBXToolsPythonProject, DBXToolsPythonWorkspace } from "./src/project-py.ts";
|
|
45
47
|
export type { PythonRepositoryOptions, PythonPackageOptions, DBXToolsPythonProjectOptions, PythonReleaseOptions, DBXToolsPythonWorkspaceOptions } from "./src/project-py.ts";
|
|
48
|
+
export { UNIFFI_RELEASE_TARGETS, DBXToolsRustProject, DBXToolsRustWorkspace } from "./src/project-rs.ts";
|
|
49
|
+
export type { CargoDependencyOptions, CargoDependency, RustPackageOptions, DBXToolsRustWorkspaceOptions, UniFFIReleaseTarget, RustBindingMapping, RustWorkspaceMapping } from "./src/project-rs.ts";
|
|
46
50
|
export { COMPILED_DIR, COMPILED_COMPILER_OPTIONS } from "./src/publish.ts";
|
|
47
51
|
export { DBXToolsRelease } from "./src/release.ts";
|
|
48
52
|
export type { StandaloneRelease, DBXToolsReleaseOptions } from "./src/release.ts";
|
|
49
53
|
export { AGNOSTIC_COMPILER_OPTIONS, PACKAGE_TAG_MIXINS } from "./src/tags.ts";
|
|
50
54
|
export type { PackageTag } from "./src/tags.ts";
|
|
51
55
|
export { DBXToolsRootTsconfig } from "./src/tsconfig.ts";
|
|
56
|
+
export { makeDefaultedInterfaceParametersOptional, addExplicitInterfaceReexports } from "./src/uniffi.ts";
|
|
57
|
+
export type { TypeScriptBindingModule } from "./src/uniffi.ts";
|
|
52
58
|
export { DBXToolsVsCode } from "./src/vscode.ts";
|
|
53
59
|
export type { IgnoreGroupOptions } from "./src/watch.ts";
|
|
54
60
|
export { VERSION_FILE, DEFAULT_VERSION } from "./src/workspace-version.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.144",
|
|
30
|
+
"@dbx-tools/path": "0.6.144",
|
|
31
|
+
"@dbx-tools/shared-core": "0.6.144",
|
|
32
32
|
"commander": "^15.0.0",
|
|
33
33
|
"concurrently": "^10.0.3",
|
|
34
34
|
"constructs": "^10.6.0",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
},
|
|
49
49
|
"main": "index.ts",
|
|
50
50
|
"license": "Apache-2.0",
|
|
51
|
-
"version": "0.6.
|
|
51
|
+
"version": "0.6.144",
|
|
52
52
|
"types": "index.ts",
|
|
53
53
|
"type": "module",
|
|
54
54
|
"exports": {
|
package/src/barrels.ts
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* rules (see {@link isExcluded}):
|
|
7
7
|
* 1. a file/folder whose name starts with `_` is private and never barrelled;
|
|
8
8
|
* 2. test / `.d.ts` files are skipped;
|
|
9
|
-
* 3. a `src/**/index.ts` is a
|
|
9
|
+
* 3. a hand-authored `src/**/index.ts` is a subpath entry, while a generated
|
|
10
|
+
* nested index is the facade for its generated folder;
|
|
10
11
|
* 4. only files that actually contain an `export` are re-exported.
|
|
11
12
|
*
|
|
12
13
|
* A hand-authored `exports.ts` sitting next to the generated `index.ts` (a Vite-style
|
|
@@ -62,15 +63,16 @@ import { isModuleFile, toPosix, recordedPackages, repoRoot } from "./packages.ts
|
|
|
62
63
|
* 1. any path segment starting with `_` (private module or folder);
|
|
63
64
|
* 2. a test / spec file;
|
|
64
65
|
* 3. a `.d.ts` declaration;
|
|
65
|
-
* 4. a `src/**/index.ts` - a
|
|
66
|
+
* 4. a hand-authored `src/**/index.ts` - a subpath entry (e.g. `src/react/index.ts`
|
|
66
67
|
* behind a package's `./react` export), not a module to namespace into the barrel.
|
|
68
|
+
* Generated nested indexes are retained as the generated folder's facade.
|
|
67
69
|
*/
|
|
68
|
-
function isExcluded(relPath: string): boolean {
|
|
70
|
+
function isExcluded(relPath: string, srcDir: string): boolean {
|
|
69
71
|
return (
|
|
70
72
|
/(^|\/)_/.test(relPath) ||
|
|
71
73
|
/\.(test|spec)\./.test(relPath) ||
|
|
72
74
|
/\.d\.ts$/.test(relPath) ||
|
|
73
|
-
/(^|\/)index\.ts$/.test(relPath)
|
|
75
|
+
(/(^|\/)index\.ts$/.test(relPath) && !isGenerated(join(srcDir, relPath)))
|
|
74
76
|
);
|
|
75
77
|
}
|
|
76
78
|
|
|
@@ -128,12 +130,14 @@ function kebabToCamel(segment: string): string {
|
|
|
128
130
|
/** Derive a valid namespace identifier from a relocated barrel module path. */
|
|
129
131
|
function modulePathToNamespace(modulePath: string): string {
|
|
130
132
|
const rel = modulePath.replace(/^\.\/src\//, "").replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
|
|
131
|
-
const segments = rel.split("/")
|
|
133
|
+
const segments = rel.split("/");
|
|
134
|
+
if (segments.at(-1) === "index" && segments.length > 1) segments.pop();
|
|
135
|
+
const names = segments.map(kebabToCamel);
|
|
132
136
|
let name =
|
|
133
|
-
|
|
134
|
-
?
|
|
135
|
-
:
|
|
136
|
-
|
|
137
|
+
names.length === 1
|
|
138
|
+
? names[0]!
|
|
139
|
+
: names[0]! +
|
|
140
|
+
names
|
|
137
141
|
.slice(1)
|
|
138
142
|
.map((s) => string.capitalize(s))
|
|
139
143
|
.join("");
|
|
@@ -315,23 +319,36 @@ function generateForPackage(pkgDir: string): number {
|
|
|
315
319
|
const before = existsSync(rootBarrel) ? readFileSync(rootBarrel, "utf8") : undefined;
|
|
316
320
|
|
|
317
321
|
// The re-exportable module set under `src/`: every source file that actually
|
|
318
|
-
// exports something, minus
|
|
319
|
-
// `src/**/index.ts`
|
|
320
|
-
//
|
|
322
|
+
// exports something, minus private / test / declaration files and hand-authored
|
|
323
|
+
// `src/**/index.ts` subpath entries. A generated nested index is retained as
|
|
324
|
+
// that generated folder's public facade. `findFiles`
|
|
321
325
|
// yields posix paths relative to `srcDir`; `hasExport` parses each via
|
|
322
326
|
// `moduleStatements` and needs the absolute path.
|
|
323
327
|
const candidates = [...find.findFiles("**/*", { cwd: srcDir })]
|
|
324
328
|
.map(toPosix)
|
|
325
|
-
.filter(
|
|
326
|
-
|
|
329
|
+
.filter(
|
|
330
|
+
(file) =>
|
|
331
|
+
isModuleFile(file) || (/(^|\/)index\.ts$/.test(file) && isGenerated(join(srcDir, file))),
|
|
332
|
+
)
|
|
333
|
+
.filter((f) => !isExcluded(f, srcDir))
|
|
327
334
|
.filter((f) => hasExport(join(srcDir, f)));
|
|
328
335
|
|
|
336
|
+
const generatedIndexDirs = new Set(
|
|
337
|
+
candidates
|
|
338
|
+
.filter((file) => /(^|\/)index\.ts$/.test(file) && isGenerated(join(srcDir, file)))
|
|
339
|
+
.map((file) => file.replace(/(^|\/)index\.ts$/, "")),
|
|
340
|
+
);
|
|
341
|
+
const publicCandidates = candidates.filter((file) => {
|
|
342
|
+
if (/(^|\/)index\.ts$/.test(file)) return true;
|
|
343
|
+
return ![...generatedIndexDirs].some((dir) => dir && file.startsWith(`${dir}/`));
|
|
344
|
+
});
|
|
345
|
+
|
|
329
346
|
// Collapse each extensionless module path to one entry, preferring a TypeScript
|
|
330
347
|
// source over a sibling compiled artifact (`math.ts` wins over a committed
|
|
331
348
|
// `math.js`), so a module is barrelled exactly once. Then sort by module path.
|
|
332
349
|
const byModulePath = new Map<string, string>();
|
|
333
|
-
for (const f of
|
|
334
|
-
const stem = f.replace(MODULE_EXT_RE, "");
|
|
350
|
+
for (const f of publicCandidates) {
|
|
351
|
+
const stem = f.replace(/(^|\/)index\.ts$/, "").replace(MODULE_EXT_RE, "");
|
|
335
352
|
const existing = byModulePath.get(stem);
|
|
336
353
|
if (!existing || (!isSourceExt(existing) && isSourceExt(f))) byModulePath.set(stem, f);
|
|
337
354
|
}
|
package/src/packages.ts
CHANGED
|
@@ -269,7 +269,7 @@ export function readPackageManifest(dir: string): Record<string, unknown> | unde
|
|
|
269
269
|
}
|
|
270
270
|
|
|
271
271
|
/** A package's `dbxToolsConfig` object, or `undefined` when absent. */
|
|
272
|
-
function readDbxToolsConfig(dir: string): Record<string, unknown> | undefined {
|
|
272
|
+
export function readDbxToolsConfig(dir: string): Record<string, unknown> | undefined {
|
|
273
273
|
const config = readPackageManifest(dir)?.dbxToolsConfig;
|
|
274
274
|
return object.isRecord(config) ? config : undefined;
|
|
275
275
|
}
|
package/src/project-predicate.ts
CHANGED
|
@@ -33,7 +33,8 @@ 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"
|
|
36
|
+
(project as Partial<DBXToolsProject>).language === "python" ||
|
|
37
|
+
(project as Partial<DBXToolsProject>).language === "rust",
|
|
37
38
|
);
|
|
38
39
|
}
|
|
39
40
|
|
package/src/project-py.ts
CHANGED
|
@@ -23,6 +23,8 @@ export interface PythonPackageOptions extends DBXToolsProjectOptions {
|
|
|
23
23
|
readonly description: string;
|
|
24
24
|
readonly dependencies?: readonly string[];
|
|
25
25
|
readonly scripts?: Readonly<Record<string, string>>;
|
|
26
|
+
/** Keep this package unpublished and out of public docs and releases. */
|
|
27
|
+
readonly private?: boolean;
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
/** Options for one projen-native Python workspace member. */
|
|
@@ -58,6 +60,8 @@ export interface DBXToolsPythonWorkspaceOptions {
|
|
|
58
60
|
readonly testPaths?: readonly string[];
|
|
59
61
|
readonly lintPaths?: readonly string[];
|
|
60
62
|
readonly ruffPerFileIgnores?: Readonly<Record<string, readonly string[]>>;
|
|
63
|
+
/** Generated Python implementation files Pyrefly should resolve but not type-check. */
|
|
64
|
+
readonly pyreflyProjectExcludes?: readonly string[];
|
|
61
65
|
readonly interpreterPath?: string | false;
|
|
62
66
|
readonly release?: boolean | PythonReleaseOptions;
|
|
63
67
|
}
|
|
@@ -171,6 +175,9 @@ export class DBXToolsPythonProject extends python.PythonProject implements DBXTo
|
|
|
171
175
|
if (pkg.scripts) {
|
|
172
176
|
this.uv.file.addOverride("project.scripts", pkg.scripts);
|
|
173
177
|
}
|
|
178
|
+
if (pkg.private) {
|
|
179
|
+
this.uv.file.addOverride("tool.dbx-tools.private", true);
|
|
180
|
+
}
|
|
174
181
|
formatPyproject(this.uv.file);
|
|
175
182
|
this.uv.file.readonly = true;
|
|
176
183
|
|
|
@@ -263,7 +270,9 @@ export class DBXToolsPythonWorkspace extends Component {
|
|
|
263
270
|
tool: {
|
|
264
271
|
uv: python.uvConfig.toJson_UvConfiguration({
|
|
265
272
|
package: false,
|
|
266
|
-
workspace: {
|
|
273
|
+
workspace: {
|
|
274
|
+
members: [`${this.repository.root}/*`],
|
|
275
|
+
},
|
|
267
276
|
}),
|
|
268
277
|
pytest: {
|
|
269
278
|
ini_options: {
|
|
@@ -283,6 +292,10 @@ export class DBXToolsPythonWorkspace extends Component {
|
|
|
283
292
|
if (options.indexStrategy) {
|
|
284
293
|
file.addOverride("tool.uv.index-strategy", options.indexStrategy);
|
|
285
294
|
}
|
|
295
|
+
file.addOverride("tool.pyrefly.ignore-errors-in-generated-code", true);
|
|
296
|
+
if (options.pyreflyProjectExcludes?.length) {
|
|
297
|
+
file.addOverride("tool.pyrefly.project-excludes", [...options.pyreflyProjectExcludes]);
|
|
298
|
+
}
|
|
286
299
|
file.addOverride(
|
|
287
300
|
"tool.uv.sources",
|
|
288
301
|
Object.fromEntries(options.packages.map((pkg) => [pkg.name, { workspace: true }])),
|
|
@@ -318,6 +331,8 @@ export class DBXToolsPythonWorkspace extends Component {
|
|
|
318
331
|
|
|
319
332
|
private addReleaseWorkflow(project: javascript.NodeProject, options: PythonReleaseOptions): void {
|
|
320
333
|
if (!project.github) return;
|
|
334
|
+
const publishablePackages = this.packages.filter((pkg) => !pkg.packageOptions.private);
|
|
335
|
+
if (publishablePackages.length === 0) return;
|
|
321
336
|
const workflow = new GithubWorkflow(project.github, options.workflowName ?? "python-release");
|
|
322
337
|
workflow.file?.addOverride("permissions", { contents: "read" });
|
|
323
338
|
workflow.on({ push: { tags: ["v*"] }, workflowDispatch: {} });
|
|
@@ -346,7 +361,7 @@ export class DBXToolsPythonWorkspace extends Component {
|
|
|
346
361
|
},
|
|
347
362
|
{
|
|
348
363
|
name: "Build distributions",
|
|
349
|
-
run:
|
|
364
|
+
run: publishablePackages
|
|
350
365
|
.map(
|
|
351
366
|
(pkg) =>
|
|
352
367
|
`uv build --package ${pkg.packageOptions.name} --out-dir dist/${pkg.packageOptions.directory}`,
|
|
@@ -356,7 +371,7 @@ export class DBXToolsPythonWorkspace extends Component {
|
|
|
356
371
|
{
|
|
357
372
|
name: "Validate distributions",
|
|
358
373
|
run: [
|
|
359
|
-
`test "$(find dist -type f \\( -name '*.whl' -o -name '*.tar.gz' \\) | wc -l | tr -d ' ')" -eq ${
|
|
374
|
+
`test "$(find dist -type f \\( -name '*.whl' -o -name '*.tar.gz' \\) | wc -l | tr -d ' ')" -eq ${publishablePackages.length * 2}`,
|
|
360
375
|
"uvx twine check dist/*/*.whl dist/*/*.tar.gz",
|
|
361
376
|
].join("\n"),
|
|
362
377
|
},
|
|
@@ -367,7 +382,7 @@ export class DBXToolsPythonWorkspace extends Component {
|
|
|
367
382
|
},
|
|
368
383
|
],
|
|
369
384
|
});
|
|
370
|
-
for (const pkg of
|
|
385
|
+
for (const pkg of publishablePackages) {
|
|
371
386
|
workflow.addJob(`publish-${pkg.packageOptions.directory}`, {
|
|
372
387
|
if: "${{ github.event_name == 'push' }}",
|
|
373
388
|
needs: ["build"],
|