@dbx-tools/projen 0.9.31 → 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 -105
- 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 -96
- 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/README.md
CHANGED
|
@@ -125,63 +125,71 @@ VS Code component; dbx-tools reuses it rather than constructing a second
|
|
|
125
125
|
|
|
126
126
|
### Bundle Node Code For PythonMonkey
|
|
127
127
|
|
|
128
|
-
Add `nodeBindings` to any `DBXToolsPythonWorkspace` package to generate
|
|
129
|
-
committed CommonJS
|
|
130
|
-
|
|
131
|
-
|
|
128
|
+
Add `nodeBindings` to any `DBXToolsPythonWorkspace` package to generate
|
|
129
|
+
committed CommonJS runtimes and Python wrappers from public Node packages. Pass
|
|
130
|
+
one binding object or an array. Projen writes one
|
|
131
|
+
`[tool.dbx_tools.node_bindings]` table or multiple
|
|
132
|
+
`[[tool.dbx_tools.node_bindings]]` tables accordingly, so generation also works
|
|
133
|
+
in consuming repositories after `@dbx-tools/projen` is installed.
|
|
132
134
|
|
|
133
135
|
```ts
|
|
134
136
|
new project.DBXToolsPythonWorkspace(root, {
|
|
135
137
|
root: "packages/py",
|
|
136
138
|
packages: [
|
|
137
139
|
{
|
|
138
|
-
directory: "
|
|
139
|
-
description: "Python bindings for example
|
|
140
|
-
nodeBindings:
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
},
|
|
151
|
-
],
|
|
152
|
-
},
|
|
140
|
+
directory: "postgres",
|
|
141
|
+
description: "Python bindings for example Postgres helpers",
|
|
142
|
+
nodeBindings: [
|
|
143
|
+
{
|
|
144
|
+
package: "@example/postgres",
|
|
145
|
+
modules: ["identity", "config"],
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
package: "@example/lakebase",
|
|
149
|
+
modules: ["address"],
|
|
150
|
+
},
|
|
151
|
+
],
|
|
153
152
|
},
|
|
154
153
|
],
|
|
155
154
|
});
|
|
156
155
|
```
|
|
157
156
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
`
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
157
|
+
Bindings always live under
|
|
158
|
+
`<python-module>/_generated/node/`. Each Python package receives one shared
|
|
159
|
+
`_runtime.js`; typed wrappers live under `<node-package>/<module>.py`. This
|
|
160
|
+
preserves Node module singleton identity across all bindings in the Python
|
|
161
|
+
process. The intermediate directories remain implicit namespace packages with
|
|
162
|
+
no generated `__init__.py`. Omit `modules` to discover every exported package
|
|
163
|
+
namespace that contains plain functions, or supply one module string or an array
|
|
164
|
+
explicitly.
|
|
165
|
+
Generated wrappers convert public function names to `snake_case`, preserve
|
|
166
|
+
synchronous functions, await promises, recursively convert plain records and
|
|
167
|
+
arrays, and proxy returned JavaScript class instances with `snake_case` async
|
|
168
|
+
methods.
|
|
168
169
|
|
|
169
170
|
Every function parameter is resolved through the TypeScript compiler API and
|
|
170
171
|
rendered with its Python-equivalent name and type. Supported record types become
|
|
171
172
|
keyword-only Python dataclasses, nested records become nested dataclasses, and a
|
|
172
173
|
same-named exported companion's `defaults()` method supplies field defaults. A
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
174
|
+
record parameter can be passed as the generated dataclass or a dictionary.
|
|
175
|
+
Generation fails with the property path when a type cannot be represented safely
|
|
176
|
+
in Python.
|
|
176
177
|
|
|
177
178
|
Return types use the same compiler model. Plain records become generated
|
|
178
179
|
`TypedDict` responses, returned clients and class instances become `Protocol`
|
|
179
180
|
types with typed async methods, and primitives, arrays, maps, promises, and
|
|
180
181
|
optional values retain their corresponding Python annotations.
|
|
181
182
|
|
|
182
|
-
The
|
|
183
|
-
|
|
184
|
-
|
|
183
|
+
The generator resolves portable Node built-ins through standard browser
|
|
184
|
+
polyfills. Python-host-backed adapters for process execution, files, crypto,
|
|
185
|
+
OS values, and process state are included automatically, so packages do not
|
|
186
|
+
configure a shim directory.
|
|
187
|
+
|
|
188
|
+
The workspace creates `<name>:python-runtime` and
|
|
189
|
+
`<name>:python-runtime:check`. Full synthesis regenerates bindings and removes
|
|
190
|
+
stale `_generated/node` trees by reconciling them with the current
|
|
191
|
+
`pyproject.toml` files, including the resynthesis triggered by a `.projenrc.ts`
|
|
192
|
+
change.
|
|
185
193
|
Built-in shims can make ordinary Node imports work under PythonMonkey.
|
|
186
194
|
`functionOverrides` replaces named exports only in the generated runtime, so the
|
|
187
195
|
Node package does not gain Python callbacks or alternate source files. A
|
|
@@ -264,15 +272,14 @@ barrels.generateBarrels();
|
|
|
264
272
|
|
|
265
273
|
`generateCodegen()` reads `package.json` `codegen.inputs` and writes generated
|
|
266
274
|
schema modules. They are written read-only, and the root ESLint task runs with
|
|
267
|
-
|
|
275
|
+
an explicit separate fix task, so each generated module is added to
|
|
268
276
|
`ignorePatterns` at synth - named individually via `codegen.codegenModulePaths()`,
|
|
269
277
|
never as a blanket `<package>/src/**`. A codegen package may hold hand-written
|
|
270
278
|
modules beside its generated ones, and those must stay linted.
|
|
271
279
|
|
|
272
280
|
`generateBarrels()` writes package-root `index.ts` barrels with module
|
|
273
|
-
namespaces, flat unique type exports, `PACKAGE_IDENTIFIER`,
|
|
274
|
-
|
|
275
|
-
modules both declare is ambiguous and stays namespace-only —
|
|
281
|
+
namespaces, flat unique type exports, and `PACKAGE_IDENTIFIER`, returning the
|
|
282
|
+
number that actually changed. A name two modules both declare is ambiguous and stays namespace-only,
|
|
276
283
|
except when one of them is generated: the hand-written module is the curated view
|
|
277
284
|
of the generated shape (`shared-genie`'s `genie-model.ts` extends its own
|
|
278
285
|
codegen'd `dashboards.ts`), so it owns the name and stays hoisted. A barrel whose export surface is unchanged is left untouched,
|
|
@@ -325,10 +332,7 @@ tree ROOT only; a member package never gets a nested one.
|
|
|
325
332
|
import { clean, watch } from "@dbx-tools/projen";
|
|
326
333
|
|
|
327
334
|
const generated = clean.listGeneratedFiles();
|
|
328
|
-
watch.watchLoop(
|
|
329
|
-
roots: watch.watchRoots(),
|
|
330
|
-
onChange: async (files) => console.log(files),
|
|
331
|
-
});
|
|
335
|
+
watch.watchLoop("workspace", watch.watchRoots(), async (files) => refresh(files));
|
|
332
336
|
```
|
|
333
337
|
|
|
334
338
|
Use these modules for maintenance tasks that should follow the same generated
|
|
@@ -346,7 +350,6 @@ file contract as the CLI.
|
|
|
346
350
|
- `barrels` / `moduleExports` - public entrypoint generation.
|
|
347
351
|
- `codegen` - `.d.ts` to zod schema generation.
|
|
348
352
|
- `openapi` - tsoa/OpenAPI package generation.
|
|
349
|
-
- `releaseCatalog` - cross-language package ownership and publication order.
|
|
350
353
|
- `bunApp` / `tsconfig` / `vscode` - generated support files/components.
|
|
351
354
|
- `generated` / `clean` / `watch` / `scaffold` - read-only file ownership,
|
|
352
355
|
cleanup, watchers, and synth orchestration.
|
|
@@ -366,24 +369,27 @@ packages in up to four `tsc --build` processes and runs custom compile tasks alo
|
|
|
366
369
|
`test` delegates with `bun run --filter '*'`. Both read the current workspace
|
|
367
370
|
list, so a new package is covered without a re-synth. Work from the root:
|
|
368
371
|
|
|
369
|
-
| Task
|
|
370
|
-
|
|
|
371
|
-
| `bun run build`
|
|
372
|
-
| `bun run compile`
|
|
373
|
-
| `bun run test`
|
|
374
|
-
| `bun run sync`
|
|
375
|
-
| `bun run barrels`
|
|
376
|
-
| `bun run bump`
|
|
377
|
-
| `bun run version:check`
|
|
378
|
-
| `bun run release`
|
|
379
|
-
|
|
380
|
-
Run exactly `bun run release` from
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
372
|
+
| Task | What it does |
|
|
373
|
+
| ----------------------- | --------------------------------------------------- |
|
|
374
|
+
| `bun run build` | synth + workspace compile and tests |
|
|
375
|
+
| `bun run compile` | Batched `tsc --build` plus custom member compiles |
|
|
376
|
+
| `bun run test` | `eslint` once, then each member's tests |
|
|
377
|
+
| `bun run sync` | re-synth (`--watch` to keep synthing) |
|
|
378
|
+
| `bun run barrels` | regenerate the read-only `index.ts` barrels |
|
|
379
|
+
| `bun run bump` | increment `VERSION` and regenerate version surfaces |
|
|
380
|
+
| `bun run version:check` | verify every generated version against `VERSION` |
|
|
381
|
+
| `bun run release` | run the configured release transaction |
|
|
382
|
+
|
|
383
|
+
Run exactly `bun run release` from any branch. Pending changes are committed and
|
|
384
|
+
the current branch is pushed first. A non-`main` branch then fast-forwards
|
|
385
|
+
`main`; the command fails without creating a merge commit if either branch has
|
|
386
|
+
diverged. From `main`, it calls the existing `bump` task, commits the generated
|
|
387
|
+
version changes, pushes `main`, and pushes the matching annotated `vX.Y.Z` tag.
|
|
388
|
+
Pass `--no-bump` only to release an existing synchronized local bump. The tag
|
|
389
|
+
workflow verifies that the tag points at the exact `origin/main` commit,
|
|
390
|
+
publishes npm and PyPI directly from that checkout, then builds and deploys
|
|
391
|
+
documentation. Local npm and Python registry publication remains available
|
|
392
|
+
through the direct local publication tooling.
|
|
387
393
|
|
|
388
394
|
Members intentionally keep only the tasks that something OTHER than a human
|
|
389
395
|
invokes, so there is no second place to run the same thing:
|
|
@@ -412,6 +418,6 @@ from the root instead.
|
|
|
412
418
|
## Versioning
|
|
413
419
|
|
|
414
420
|
`VERSION` is the language-neutral version seam. Real package manifests remain
|
|
415
|
-
Projen-owned and reproduce it exactly across JavaScript and Python packages.
|
|
416
|
-
|
|
417
|
-
|
|
421
|
+
Projen-owned and reproduce it exactly across JavaScript and Python packages.
|
|
422
|
+
Release jobs publish the generated workspace inventory in dependency-safe
|
|
423
|
+
workflow order without introducing another version owner.
|
package/index.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Hand edits are overwritten on the next watch; this file is read-only.
|
|
4
4
|
|
|
5
5
|
export const PACKAGE_IDENTIFIER = "@dbx-tools/projen";
|
|
6
|
-
export const PACKAGE_VERSION = "0.9.
|
|
6
|
+
export const PACKAGE_VERSION = "0.9.36";
|
|
7
7
|
export * as barrels from "./src/barrels.ts";
|
|
8
8
|
export * as bunApp from "./src/bun-app.ts";
|
|
9
9
|
export * as bunWorkflow from "./src/bun-workflow.ts";
|
|
@@ -26,7 +26,6 @@ export * as publish from "./src/publish.ts";
|
|
|
26
26
|
export * as pythonNodeBindings from "./src/python-node-bindings.ts";
|
|
27
27
|
export * as pythonNodeBundle from "./src/python-node-bundle.ts";
|
|
28
28
|
export * as release from "./src/release.ts";
|
|
29
|
-
export * as releaseCatalog from "./src/release-catalog.ts";
|
|
30
29
|
export * as releaseContext from "./src/release-context.ts";
|
|
31
30
|
export * as scaffold from "./src/scaffold.ts";
|
|
32
31
|
export * as tags from "./src/tags.ts";
|
|
@@ -50,23 +49,21 @@ export { PnpmWorkspaceState } from "./src/pnpm-workspace.ts";
|
|
|
50
49
|
export type { Catalog, AllowBuilds, DBXToolsPNPMWorkspaceOptions } from "./src/pnpm-workspace.ts";
|
|
51
50
|
export type { DBXToolsProjectLanguage, DBXToolsProjectOptions, DBXToolsProject, ApplyToProjectsOptions } from "./src/project.ts";
|
|
52
51
|
export { PackageIdentifier, DBX_TOOLS_LICENSE, DBXToolsNodeProject, ROOT_INSTALL_ONLY_MIXIN, DBXToolsTypeScriptProject } from "./src/project-js.ts";
|
|
53
|
-
export type { DBXToolsJavaScriptProject, DBXToolsReleaseMode,
|
|
52
|
+
export type { DBXToolsJavaScriptProject, DBXToolsReleaseMode, DBXToolsJavaScriptProjectOptions, DBXToolsTypeScriptProjectOptions } from "./src/project-js.ts";
|
|
54
53
|
export { DBXToolsPythonProject, DBXToolsPythonWorkspace } from "./src/project-py.ts";
|
|
55
|
-
export type { PythonRepositoryOptions, PythonPackageOptions,
|
|
54
|
+
export type { PythonRepositoryOptions, PythonPackageOptions, DBXToolsPythonProjectOptions, PythonReleaseOptions, DBXToolsPythonWorkspaceOptions } from "./src/project-py.ts";
|
|
56
55
|
export { PROJEN_VERSION } from "./src/projen-version.ts";
|
|
57
56
|
export { COMPILED_DIR, COMPILED_COMPILER_OPTIONS } from "./src/publish.ts";
|
|
57
|
+
export { PYTHON_NODE_SHIM_ROOT } from "./src/python-node-bindings.ts";
|
|
58
58
|
export type { ResolvedPythonNodeFunctionOverride, ResolvedPythonNodeBindings } from "./src/python-node-bindings.ts";
|
|
59
59
|
export { PythonNodeBundle } from "./src/python-node-bundle.ts";
|
|
60
60
|
export type { PythonNodeFunctionOverride, PythonNodeBindingsOptions, PythonNodeBundleOptions } from "./src/python-node-bundle.ts";
|
|
61
61
|
export { DBXToolsRelease } from "./src/release.ts";
|
|
62
|
-
export type {
|
|
63
|
-
export { DBXToolsReleaseCatalog } from "./src/release-catalog.ts";
|
|
64
|
-
export type { ReleaseProjectLanguage, ReleaseArtifactKind, ReleaseEdgeKind, ReleasePropagation, ReleaseUnitRule, DBXToolsReleaseCatalogOptions, ReleaseDependencyInput, ReleaseProjectRegistration, ExternalReleaseProjectRegistration, ReleaseArtifactRegistration, ReleaseProjectNode, ReleaseArtifact, ReleaseDependencyEdge, ReleaseUnit, ReleaseUnitGraph } from "./src/release-catalog.ts";
|
|
62
|
+
export type { ReleaseDocsOptions, DBXToolsReleaseOptions } from "./src/release.ts";
|
|
65
63
|
export { RELEASE_TAG, RELEASE_SHA, RELEASE_VERSION } from "./src/release-context.ts";
|
|
66
64
|
export { AGNOSTIC_COMPILER_OPTIONS, PACKAGE_TAG_MIXINS } from "./src/tags.ts";
|
|
67
65
|
export type { PackageTag } from "./src/tags.ts";
|
|
68
66
|
export { DBXToolsRootTsconfig } from "./src/tsconfig.ts";
|
|
69
|
-
export { DBXToolsVsCode } from "./src/vscode.ts";
|
|
70
67
|
export type { IgnoreGroupOptions, WatchLoopOptions } from "./src/watch.ts";
|
|
71
68
|
export type { WorkspaceMutationLockOptions } from "./src/workspace-lock.ts";
|
|
72
69
|
export { VERSION_FILE, DEFAULT_VERSION } from "./src/workspace-version.ts";
|
package/package.json
CHANGED
|
@@ -24,28 +24,28 @@
|
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/node": "^24.6.0",
|
|
26
26
|
"@types/semver": "^7.7.1",
|
|
27
|
-
"constructs": "^10.0.0",
|
|
28
27
|
"projen": "0.103.27",
|
|
29
|
-
"tsoa": "^6.6.0"
|
|
30
|
-
"typescript": "^5.9.3"
|
|
28
|
+
"tsoa": "^6.6.0"
|
|
31
29
|
},
|
|
32
30
|
"peerDependencies": {
|
|
33
31
|
"projen": "^0.103.27"
|
|
34
32
|
},
|
|
35
33
|
"dependencies": {
|
|
36
34
|
"@clack/prompts": "^1.7.0",
|
|
37
|
-
"@dbx-tools/core": "^0.9.
|
|
38
|
-
"@dbx-tools/path": "^0.9.
|
|
39
|
-
"@dbx-tools/shared-core": "^0.9.
|
|
35
|
+
"@dbx-tools/core": "^0.9.36",
|
|
36
|
+
"@dbx-tools/path": "^0.9.36",
|
|
37
|
+
"@dbx-tools/shared-core": "^0.9.36",
|
|
40
38
|
"commander": "^15.0.0",
|
|
41
39
|
"concurrently": "^10.0.3",
|
|
42
40
|
"constructs": "^10.6.0",
|
|
43
41
|
"is-identifier": "^1",
|
|
42
|
+
"node-stdlib-browser": "^1.3.1",
|
|
44
43
|
"openapi-typescript": "^7.13.0",
|
|
45
44
|
"oxc-parser": "^0.90.0",
|
|
46
45
|
"semver": "^7.7.3",
|
|
47
46
|
"smol-toml": "1.8.0",
|
|
48
47
|
"ts-to-zod": "^5.1.0",
|
|
48
|
+
"typescript": "^5.9.3",
|
|
49
49
|
"yaml": "^2.9.0"
|
|
50
50
|
},
|
|
51
51
|
"devEngines": {
|
|
@@ -56,11 +56,12 @@
|
|
|
56
56
|
},
|
|
57
57
|
"main": "index.ts",
|
|
58
58
|
"license": "Apache-2.0",
|
|
59
|
-
"version": "0.9.
|
|
59
|
+
"version": "0.9.36",
|
|
60
60
|
"types": "index.ts",
|
|
61
61
|
"type": "module",
|
|
62
62
|
"exports": {
|
|
63
63
|
".": "./index.ts",
|
|
64
|
+
"./release-packaging": "./tasks/lib/publish-npm.ts",
|
|
64
65
|
"./package.json": "./package.json"
|
|
65
66
|
},
|
|
66
67
|
"files": [
|
package/src/_task-command.ts
CHANGED
|
@@ -12,13 +12,6 @@ export interface CaptureTaskCommandOptions extends TaskCommandOptions {
|
|
|
12
12
|
readonly check?: boolean;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export type ProbeTaskCommandOptions = TaskCommandOptions;
|
|
16
|
-
|
|
17
|
-
export interface LoggedTaskCommandOptions extends TaskCommandOptions {
|
|
18
|
-
/** Receive normalized stdout and stderr lines after the command exits. */
|
|
19
|
-
readonly onLine: (line: string) => void;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
15
|
function executable(command: string): string {
|
|
23
16
|
return command === "bun" && process.versions.bun ? process.execPath : command;
|
|
24
17
|
}
|
|
@@ -76,47 +69,6 @@ export function captureTaskCommand(
|
|
|
76
69
|
return result.exitCode === 0 ? (result.stdout?.trim() ?? "") : "";
|
|
77
70
|
}
|
|
78
71
|
|
|
79
|
-
/** Capture trimmed stdout while preserving command failure as `undefined`. */
|
|
80
|
-
export function probeTaskCommand(
|
|
81
|
-
cwd: string,
|
|
82
|
-
command: string,
|
|
83
|
-
args: readonly string[],
|
|
84
|
-
options: ProbeTaskCommandOptions = {},
|
|
85
|
-
): string | undefined {
|
|
86
|
-
const result = exec.spawnSync(executable(command), [...args], {
|
|
87
|
-
cwd,
|
|
88
|
-
env: options.env,
|
|
89
|
-
stdout: "capture",
|
|
90
|
-
stderr: options.stderr ?? "ignore",
|
|
91
|
-
stdin: "ignore",
|
|
92
|
-
check: false,
|
|
93
|
-
});
|
|
94
|
-
return result.exitCode === 0 ? (result.stdout?.trim() ?? "") : undefined;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/** Run a checked command while routing captured output through one line policy. */
|
|
98
|
-
export function runLoggedTaskCommand(
|
|
99
|
-
cwd: string,
|
|
100
|
-
command: string,
|
|
101
|
-
args: readonly string[],
|
|
102
|
-
options: LoggedTaskCommandOptions,
|
|
103
|
-
): void {
|
|
104
|
-
const result = exec.spawnSync(executable(command), [...args], {
|
|
105
|
-
cwd,
|
|
106
|
-
env: options.env,
|
|
107
|
-
stdout: "capture",
|
|
108
|
-
stderr: "capture",
|
|
109
|
-
stdin: "ignore",
|
|
110
|
-
check: false,
|
|
111
|
-
});
|
|
112
|
-
for (const line of [...result.stdoutLines, ...result.stderrLines]) {
|
|
113
|
-
if (line) options.onLine(line);
|
|
114
|
-
}
|
|
115
|
-
if (result.exitCode !== 0) {
|
|
116
|
-
throw new Error(`${command} exited with ${result.exitCode}`);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
72
|
/** Whether a quiet, non-interactive command exits successfully. */
|
|
121
73
|
export function taskCommandSucceeds(
|
|
122
74
|
cwd: string,
|
|
@@ -135,30 +87,3 @@ export function taskCommandSucceeds(
|
|
|
135
87
|
}).exitCode === 0
|
|
136
88
|
);
|
|
137
89
|
}
|
|
138
|
-
|
|
139
|
-
/** Run Git with the shared non-interactive checked command policy. */
|
|
140
|
-
export function runGitTaskCommand(
|
|
141
|
-
cwd: string,
|
|
142
|
-
args: readonly string[],
|
|
143
|
-
options: TaskCommandOptions = {},
|
|
144
|
-
): void {
|
|
145
|
-
runTaskCommand(cwd, "git", args, options);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/** Capture Git output, returning an empty string for optional offline probes. */
|
|
149
|
-
export function captureGitTaskCommand(
|
|
150
|
-
cwd: string,
|
|
151
|
-
args: readonly string[],
|
|
152
|
-
options: CaptureTaskCommandOptions = {},
|
|
153
|
-
): string {
|
|
154
|
-
return captureTaskCommand(cwd, "git", args, options);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/** Whether a quiet Git command succeeds. */
|
|
158
|
-
export function gitTaskCommandSucceeds(
|
|
159
|
-
cwd: string,
|
|
160
|
-
args: readonly string[],
|
|
161
|
-
options: Pick<TaskCommandOptions, "env"> = {},
|
|
162
|
-
): boolean {
|
|
163
|
-
return taskCommandSucceeds(cwd, "git", args, options);
|
|
164
|
-
}
|
package/src/barrels.ts
CHANGED
|
@@ -29,10 +29,8 @@
|
|
|
29
29
|
* `export { ... }`. The module namespaces stay either way, so a namespaced call
|
|
30
30
|
* site keeps working.
|
|
31
31
|
*
|
|
32
|
-
* Every generated barrel also exports `PACKAGE_IDENTIFIER` and
|
|
33
|
-
*
|
|
34
|
-
* retain package identity without trying to recover it from an ESM namespace
|
|
35
|
-
* object or loader function.
|
|
32
|
+
* Every generated barrel also exports `PACKAGE_IDENTIFIER` and `PACKAGE_VERSION`
|
|
33
|
+
* from the package's own `package.json`.
|
|
36
34
|
*
|
|
37
35
|
* Uniqueness is tallied over types and values TOGETHER: a name carried by two
|
|
38
36
|
* modules is ambiguous whichever kind it is, and hoisting one module's value
|
|
@@ -111,10 +109,10 @@ const BARREL_HEADER: HeaderOpts = {
|
|
|
111
109
|
|
|
112
110
|
/** Generated package metadata exports, reserved against source-module hoisting. */
|
|
113
111
|
const PACKAGE_IDENTIFIER_EXPORT = "PACKAGE_IDENTIFIER";
|
|
114
|
-
const PACKAGE_VERSION_EXPORT = "PACKAGE_VERSION";
|
|
115
112
|
const PACKAGE_IDENTIFIER_LINE = `export const ${PACKAGE_IDENTIFIER_EXPORT} = "";`;
|
|
116
|
-
const PACKAGE_VERSION_LINE = `export const ${PACKAGE_VERSION_EXPORT} = "";`;
|
|
117
113
|
const PACKAGE_IDENTIFIER_LINE_RE = /^export const PACKAGE_IDENTIFIER = .*;$/m;
|
|
114
|
+
const PACKAGE_VERSION_EXPORT = "PACKAGE_VERSION";
|
|
115
|
+
const PACKAGE_VERSION_LINE = `export const ${PACKAGE_VERSION_EXPORT} = "";`;
|
|
118
116
|
const PACKAGE_VERSION_LINE_RE = /^export const PACKAGE_VERSION = .*;$/m;
|
|
119
117
|
|
|
120
118
|
/** `config-tools` / `config_tools` -> `configTools`; `local-fs` -> `localFS`. */
|
|
@@ -123,7 +121,8 @@ function moduleSegmentToCamel(segment: string): string {
|
|
|
123
121
|
...stringUtils.tokenizeWithOptions({ lowerCase: true, capitalize: true }, segment),
|
|
124
122
|
];
|
|
125
123
|
if (tokens.length === 0) return segment;
|
|
126
|
-
const
|
|
124
|
+
const first = tokens[0]!;
|
|
125
|
+
const rest = tokens.slice(1);
|
|
127
126
|
// Standalone acronym modules (`fs`, `ai`) stay lowercase so they match
|
|
128
127
|
// Node-style namespaces (`import * as fs`). Trailing / mid acronyms keep
|
|
129
128
|
// their override casing (`localFS`).
|
|
@@ -274,7 +273,7 @@ function customExportNames(file: string): Set<string> {
|
|
|
274
273
|
*/
|
|
275
274
|
function mergeCustomExports(content: string, pkgDir: string): string {
|
|
276
275
|
const customPath = join(pkgDir, CUSTOM_EXPORTS_FILE);
|
|
277
|
-
if (!existsSync(customPath)
|
|
276
|
+
if (!existsSync(customPath)) return content;
|
|
278
277
|
const overridden = customExportNames(customPath);
|
|
279
278
|
const kept = content.split("\n").filter((line) => {
|
|
280
279
|
const ns = /^export \* as (\w+) from /.exec(line)?.[1];
|
|
@@ -283,12 +282,11 @@ function mergeCustomExports(content: string, pkgDir: string): string {
|
|
|
283
282
|
return `${kept.join("\n").replace(/\n+$/, "")}\nexport * from "./exports.ts";\n`;
|
|
284
283
|
}
|
|
285
284
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
function packageMetadata(pkgDir: string): { identifier: string; version: string } {
|
|
285
|
+
/** Read the authoritative npm package metadata emitted by the package project. */
|
|
286
|
+
function packageMetadata(pkgDir: string): {
|
|
287
|
+
identifier: string;
|
|
288
|
+
version: string;
|
|
289
|
+
} {
|
|
292
290
|
const manifestPath = join(pkgDir, "package.json");
|
|
293
291
|
const manifest = existsSync(manifestPath)
|
|
294
292
|
? json.parseRecord(readFileSync(manifestPath, "utf8"))
|
|
@@ -404,10 +402,7 @@ function generateForPackage(pkgDir: string): number {
|
|
|
404
402
|
// Hoist package-unique named exports to the top level. Names a hand-authored
|
|
405
403
|
// `exports.ts` declares are suppressed so that file stays authoritative.
|
|
406
404
|
const customPath = join(pkgDir, CUSTOM_EXPORTS_FILE);
|
|
407
|
-
const suppress =
|
|
408
|
-
existsSync(customPath) && !isLegacyBindingsExport(customPath)
|
|
409
|
-
? customExportNames(customPath)
|
|
410
|
-
: new Set<string>();
|
|
405
|
+
const suppress = existsSync(customPath) ? customExportNames(customPath) : new Set<string>();
|
|
411
406
|
suppress.add(PACKAGE_IDENTIFIER_EXPORT);
|
|
412
407
|
suppress.add(PACKAGE_VERSION_EXPORT);
|
|
413
408
|
content = hoistUniqueExports(content, pkgDir, suppress);
|
|
@@ -415,11 +410,8 @@ function generateForPackage(pkgDir: string): number {
|
|
|
415
410
|
content = mergeCustomExports(content, pkgDir);
|
|
416
411
|
|
|
417
412
|
// Package metadata is blanked for the structural comparison, then restored
|
|
418
|
-
// from package.json
|
|
419
|
-
//
|
|
420
|
-
// the file and its read-only bit untouched and report no change (0). This
|
|
421
|
-
// keeps the watcher quiet on ordinary in-file edits while allowing a version
|
|
422
|
-
// bump to update PACKAGE_VERSION.
|
|
413
|
+
// from package.json. If the result matches what's on disk, leave the file and
|
|
414
|
+
// its read-only bit untouched and report no change.
|
|
423
415
|
content = `${content.replace(/\n+$/, "")}\n`;
|
|
424
416
|
const template = `${header(BARREL_HEADER)}\n${content}`;
|
|
425
417
|
const next = withPackageMetadata(template, pkgDir);
|
package/src/bun-app.ts
CHANGED
|
@@ -14,10 +14,9 @@
|
|
|
14
14
|
* JS API, NOT the `bun build` CLI, which does not load it), plus Vite-compatible
|
|
15
15
|
* staging of an optional `public/` directory.
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
* `bun-
|
|
19
|
-
*
|
|
20
|
-
* package tweaks its server/bundle WITHOUT editing the projen-owned file.
|
|
17
|
+
* The dev and build scripts support unmanaged `bun-dev.override.ts` and
|
|
18
|
+
* `bun-build.override.ts` modules beside them. Each script imports the override's
|
|
19
|
+
* default export and merges it over the generated options.
|
|
21
20
|
*
|
|
22
21
|
* bun runs these `.ts` files directly. Being package-ROOT files (not under
|
|
23
22
|
* `src/`), they are outside the package's `tsconfig` include, so their `Bun.*`
|
|
@@ -30,8 +29,8 @@ import { type Project, TextFile } from "projen";
|
|
|
30
29
|
export const BUN_DEV_OVERRIDE = "bun-dev.override.ts";
|
|
31
30
|
/** Unmanaged build override module, merged over the generated options if present. */
|
|
32
31
|
export const BUN_BUILD_OVERRIDE = "bun-build.override.ts";
|
|
33
|
-
/** Unmanaged
|
|
34
|
-
export const BUN_APP_OVERRIDES = [
|
|
32
|
+
/** Unmanaged generated-script overrides excluded from formatting and linting. */
|
|
33
|
+
export const BUN_APP_OVERRIDES = [BUN_DEV_OVERRIDE, BUN_BUILD_OVERRIDE];
|
|
35
34
|
|
|
36
35
|
/**
|
|
37
36
|
* Root `bunfig.toml`: pin bun's HOISTED linker workspace-wide.
|
package/src/clean.ts
CHANGED
|
@@ -5,13 +5,9 @@
|
|
|
5
5
|
* `@clack/prompts` multiselect picker with all preselected, the TTY guard) lives in
|
|
6
6
|
* `tasks/clean.ts`, which forwards to these functions.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* file under the repo is a clean target - EXCEPT anything inside a dot-prefixed folder
|
|
12
|
-
* (`.projen`, `.vscode`, `.git`, ...) and `.gitignore` itself, which clean always leaves
|
|
13
|
-
* alone (projen re-syncs `.projen`/`.vscode` on the next synth). `node_modules` is
|
|
14
|
-
* enumerated separately (see {@link listNodeModulesDirs}) as whole directories.
|
|
8
|
+
* Generated files come from Projen's `.projen/files.json` inventories plus the
|
|
9
|
+
* durable header used by dynamic dbx-tools generators. File mode is edit
|
|
10
|
+
* protection only and is never treated as ownership evidence.
|
|
15
11
|
*
|
|
16
12
|
* Deleting only generated files is never destructive to the ability to regenerate:
|
|
17
13
|
* `.projenrc.ts` imports the engine by SOURCE path (relative into the repo, e.g.
|
|
@@ -21,10 +17,11 @@
|
|
|
21
17
|
* `bun install` first - the engine's runtime deps live there - so a clean that takes
|
|
22
18
|
* `node_modules` must be followed by reinstall, then re-synth.
|
|
23
19
|
*/
|
|
24
|
-
import { existsSync, rmSync, statSync } from "node:fs";
|
|
25
|
-
import { basename, join, relative } from "node:path";
|
|
20
|
+
import { existsSync, readFileSync, rmSync, statSync } from "node:fs";
|
|
21
|
+
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
26
22
|
import { find } from "@dbx-tools/path";
|
|
27
|
-
import {
|
|
23
|
+
import { json } from "@dbx-tools/shared-core";
|
|
24
|
+
import { isGenerated, makeWritable } from "./generated.ts";
|
|
28
25
|
import { resolveRepoRoot, toPosix } from "./packages.ts";
|
|
29
26
|
|
|
30
27
|
/**
|
|
@@ -35,16 +32,41 @@ import { resolveRepoRoot, toPosix } from "./packages.ts";
|
|
|
35
32
|
const CLEAN_SKIP_FILES: ReadonlySet<string> = new Set([".gitignore"]);
|
|
36
33
|
|
|
37
34
|
/**
|
|
38
|
-
* Every generated
|
|
35
|
+
* Every generated file in the workspace, as absolute paths sorted by
|
|
39
36
|
* repo-relative posix path. Skips vendor/build/VCS dirs via node-path's built-in
|
|
40
37
|
* ignores AND every dot-prefixed folder (`.projen`, `.vscode`, `.github`, ...), and
|
|
41
38
|
* {@link CLEAN_SKIP_FILES} entry (`.gitignore`).
|
|
42
39
|
*/
|
|
40
|
+
function nativeGeneratedFiles(root: string): string[] {
|
|
41
|
+
const files = new Set<string>();
|
|
42
|
+
for (const manifestPath of find.findFiles("**/.projen/files.json", {
|
|
43
|
+
cwd: root,
|
|
44
|
+
ignoreOptions: { dot: false },
|
|
45
|
+
})) {
|
|
46
|
+
const manifest = json.parseRecord(readFileSync(resolve(root, manifestPath), "utf8"));
|
|
47
|
+
const projectRoot = dirname(dirname(resolve(root, manifestPath)));
|
|
48
|
+
const entries = manifest?.files;
|
|
49
|
+
if (!Array.isArray(entries)) continue;
|
|
50
|
+
for (const entry of entries) {
|
|
51
|
+
if (typeof entry === "string") files.add(resolve(projectRoot, entry));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return [...files];
|
|
55
|
+
}
|
|
56
|
+
|
|
43
57
|
export function listGeneratedFiles(root: string = resolveRepoRoot()): string[] {
|
|
44
58
|
const rel = (f: string): string => toPosix(relative(root, f));
|
|
45
|
-
|
|
59
|
+
const custom = [...find.findFiles("**/*", { cwd: root })]
|
|
46
60
|
.map((f) => join(root, f))
|
|
47
|
-
.filter(
|
|
61
|
+
.filter(isGenerated);
|
|
62
|
+
return [...new Set([...nativeGeneratedFiles(root), ...custom])]
|
|
63
|
+
.filter(existsSync)
|
|
64
|
+
.filter(
|
|
65
|
+
(file) =>
|
|
66
|
+
!rel(file)
|
|
67
|
+
.split("/")
|
|
68
|
+
.some((part) => part.startsWith(".")),
|
|
69
|
+
)
|
|
48
70
|
.filter((f) => !CLEAN_SKIP_FILES.has(basename(f)))
|
|
49
71
|
.sort((a, b) => rel(a).localeCompare(rel(b)));
|
|
50
72
|
}
|
|
@@ -74,15 +96,19 @@ export function listNodeModulesDirs(root: string = resolveRepoRoot()): string[]
|
|
|
74
96
|
*/
|
|
75
97
|
export function removePaths(paths: readonly string[]): number {
|
|
76
98
|
let removed = 0;
|
|
99
|
+
const failures: unknown[] = [];
|
|
77
100
|
for (const path of paths) {
|
|
78
101
|
try {
|
|
79
102
|
if (!existsSync(path)) continue;
|
|
80
103
|
if (statSync(path).isFile()) makeWritable(path);
|
|
81
104
|
rmSync(path, { recursive: true, force: true });
|
|
82
105
|
removed++;
|
|
83
|
-
} catch {
|
|
84
|
-
|
|
106
|
+
} catch (error) {
|
|
107
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") failures.push(error);
|
|
85
108
|
}
|
|
86
109
|
}
|
|
110
|
+
if (failures.length > 0) {
|
|
111
|
+
throw new AggregateError(failures, `Could not remove ${failures.length} selected path(s)`);
|
|
112
|
+
}
|
|
87
113
|
return removed;
|
|
88
114
|
}
|
package/src/codegen.ts
CHANGED
|
@@ -49,7 +49,7 @@ import { basename, dirname, join, resolve } from "node:path";
|
|
|
49
49
|
import { log, object } from "@dbx-tools/shared-core";
|
|
50
50
|
import type * as ts from "typescript";
|
|
51
51
|
import { lazyRequire } from "./_lazy-require.ts";
|
|
52
|
-
import { header,
|
|
52
|
+
import { header, makeReadonly, makeWritable } from "./generated.ts";
|
|
53
53
|
import { readPackageManifest, recordedPackages, resolveRepoRoot } from "./packages.ts";
|
|
54
54
|
|
|
55
55
|
const logger = log.logger("projen:codegen");
|
|
@@ -266,11 +266,10 @@ function inlineInferredTypes(inferredFile: string): string {
|
|
|
266
266
|
.trim();
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
-
/** True if `<srcDir>/<file>`
|
|
269
|
+
/** True if `<srcDir>/<file>` carries the codegen ownership header. */
|
|
270
270
|
function isGeneratedModule(srcDir: string, file: string): boolean {
|
|
271
271
|
if (!file.endsWith(".ts")) return false;
|
|
272
272
|
const path = join(srcDir, file);
|
|
273
|
-
if (!isReadonly(path)) return false;
|
|
274
273
|
try {
|
|
275
274
|
return readFileSync(path, "utf8").startsWith("// GENERATED by projen synth (codegen:");
|
|
276
275
|
} catch {
|