@dbx-tools/projen 0.9.152 → 0.9.154
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 +31 -12
- package/index.ts +3 -2
- package/package.json +7 -6
- package/src/appkit-workspace.ts +41 -6
- package/src/bun-app.ts +7 -1
- package/src/generated.ts +16 -7
- package/src/packages.ts +6 -1
- package/src/pnpm-workspace.ts +1 -1
- package/src/project-js.ts +20 -4
- package/src/project-py.ts +52 -19
- package/src/release-context.ts +4 -0
- package/src/release.ts +131 -11
- package/src/tsconfig.ts +9 -2
- package/src/workspace-manifest.ts +10 -1
- package/tasks/appkit-deploy.ts +192 -17
- package/tasks/publish-npm.ts +5 -3
- package/tasks/pypi-trusted-publisher-instructions.ts +54 -0
- package/tasks/python-node-bindings.ts +11 -7
- package/tasks/verify-publications.ts +269 -0
package/README.md
CHANGED
|
@@ -24,10 +24,13 @@ packages. It can also generate Zod schemas from declarations and coordinate
|
|
|
24
24
|
generated-file cleanup and watch tasks.
|
|
25
25
|
|
|
26
26
|
Every `src`-bearing folder under the configured roots becomes a
|
|
27
|
-
`DBXToolsTypeScriptProject`.
|
|
27
|
+
`DBXToolsTypeScriptProject`. `packages/js` is selected automatically when it
|
|
28
|
+
exists; otherwise discovery uses `packages`. Folder path drives package name and
|
|
29
|
+
runtime tags.
|
|
28
30
|
Use `packageDefaults` for native options shared by every discovered package,
|
|
29
31
|
such as `{ publishable: false }`; reserve `resolvePackageOptions` for genuine
|
|
30
|
-
per-package differences.
|
|
32
|
+
per-package differences. Use `rootTsconfig` only for repository-wide compiler
|
|
33
|
+
differences; root tests, Bun/Node types, and React JSX are already included.
|
|
31
34
|
|
|
32
35
|
Use `DBXToolsTypeScriptProject` itself as the root when one package needs the
|
|
33
36
|
same Bun workspace, repository, task, and release defaults without a separate
|
|
@@ -107,7 +110,11 @@ new projectPy.DBXToolsPythonWorkspace(root, {
|
|
|
107
110
|
```
|
|
108
111
|
|
|
109
112
|
Distribution names and modules come from the parent scope plus each package
|
|
110
|
-
directory.
|
|
113
|
+
directory. Direct children of the Python root that contain `src/` are discovered
|
|
114
|
+
automatically; `packages` only needs entries whose metadata differs from the
|
|
115
|
+
generated defaults. Set `discoverPackages: false` for a fully explicit list.
|
|
116
|
+
The repository comes from the parent project metadata, existing generated root
|
|
117
|
+
manifest, or Git remote.
|
|
111
118
|
`internalDependencies` renders standalone Git `#subdirectory=` requirements
|
|
112
119
|
without repeating repository coordinates. Pass `repository`, `name`, or
|
|
113
120
|
`module` only to override those conventions. `root.vscode` is Projen's existing
|
|
@@ -315,13 +322,18 @@ parameter to `Project`). Drop to `mixin.create(predicate, fn)` +
|
|
|
315
322
|
Built-in tag mixins set runtime defaults for `shared`, `node`, `cli`, `server`,
|
|
316
323
|
`ui`, and `app`. Repo-specific mixins layer package-specific dependencies,
|
|
317
324
|
scripts, and generated files on top. `ui` is for reusable React component
|
|
318
|
-
libraries. `app` owns a Bun-built browser application.
|
|
325
|
+
libraries. `app` owns a Bun-built browser application. Terminal
|
|
326
|
+
`packages/**/app` and `packages/**/server` directories select those specific
|
|
327
|
+
roles instead of inheriting a generic `ui` or `node` ancestor.
|
|
319
328
|
|
|
320
329
|
AppKit mode defaults to `auto`: when the workspace has both `app` and `server`
|
|
321
330
|
packages, the engine adds the AppKit, Vite, React plugin, and Tailwind
|
|
322
331
|
dependencies to the package that uses each one. It also disables AppKit's
|
|
323
332
|
package migration, includes the Vite and generated-type inputs in the browser
|
|
324
|
-
tsconfig,
|
|
333
|
+
tsconfig, generates a read-only monorepo-safe `vite.config.ts`, adds
|
|
334
|
+
`appkit:sync`, and ignores local Databricks state. Put repository-specific Vite
|
|
335
|
+
settings in the optional unmanaged `vite.config.override.ts`; the generated
|
|
336
|
+
config merges it over the engine defaults. Set
|
|
325
337
|
`appkit: false` to disable this ownership. Set `appkit: true` to require AppKit
|
|
326
338
|
and fail synthesis when either package role or its required entry files are
|
|
327
339
|
missing. Keep application-specific UI and server dependencies in the consuming
|
|
@@ -338,8 +350,12 @@ with concrete versions and keeps every manifest stable through upload. The
|
|
|
338
350
|
generated working tree is never modified. The task passes an explicit profile,
|
|
339
351
|
using the supplied flag or resolving one through `@dbx-tools/auth`, and fails
|
|
340
352
|
before staging when neither profile nor complete environment credentials are
|
|
341
|
-
available.
|
|
342
|
-
|
|
353
|
+
available. It validates the bundle strictly, removes only stale direct-engine
|
|
354
|
+
state from the temporary snapshot, and defaults to active-run protection while
|
|
355
|
+
skipping duplicate npm validation and tests. If deployment or app startup
|
|
356
|
+
fails, the task prints the current Databricks App logs before returning the
|
|
357
|
+
original failure. A repository-owned deployment flow can run its own Bun checks
|
|
358
|
+
and spawn `appkit:deploy` for the upload step instead of maintaining staging or
|
|
343
359
|
materialization code.
|
|
344
360
|
|
|
345
361
|
Every workspace root exposes a generic development watcher:
|
|
@@ -585,11 +601,14 @@ site. Install validation prerequisites through `releaseSetupSteps`.
|
|
|
585
601
|
Publication jobs download the resulting artifacts rather than rebuilding the
|
|
586
602
|
workspace. Each Python package retains its own publishing environment and waits
|
|
587
603
|
for its package dependencies to publish. The npm job uses the bundled archive
|
|
588
|
-
publisher without a checkout or workspace install.
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
the
|
|
604
|
+
publisher without a checkout or workspace install. After all selected public
|
|
605
|
+
publishers finish, one verifier downloads the complete release artifact set and
|
|
606
|
+
polls only unresolved packages concurrently. It validates npm tarball contents
|
|
607
|
+
and PyPI distribution hashes before Pages deploys the already built site. Local
|
|
608
|
+
npm and Python registry publication remains available through the direct local
|
|
609
|
+
publication tooling and does not wait for registry propagation. Those local
|
|
610
|
+
publishers run concurrently; a failure or release termination stops every live
|
|
611
|
+
publisher before the command returns.
|
|
593
612
|
|
|
594
613
|
The default release publishes npm, Python, and docs, then publishes to
|
|
595
614
|
configured local registries. Use `bun run release --help` or the generated
|
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.154";
|
|
7
7
|
export * as appkitWorkspace from "./src/appkit-workspace.ts";
|
|
8
8
|
export * as barrels from "./src/barrels.ts";
|
|
9
9
|
export * as bunApp from "./src/bun-app.ts";
|
|
@@ -69,7 +69,7 @@ export { PYTHON_NODE_RUNTIME_DISTRIBUTION, PYTHON_NODE_RUNTIME_MODULE, PYTHON_NO
|
|
|
69
69
|
export type { PythonNodeRuntimeOptions } from "./src/python-node-runtime.ts";
|
|
70
70
|
export { DBXToolsRelease } from "./src/release.ts";
|
|
71
71
|
export type { ReleaseDocsOptions, DBXToolsReleaseOptions } from "./src/release.ts";
|
|
72
|
-
export { RELEASE_VERSION, RELEASE_WORKFLOW_FILE } from "./src/release-context.ts";
|
|
72
|
+
export { RELEASE_VERSION, RELEASE_WORKFLOW_FILE, PUBLIC_NPM_REGISTRY_URL, PUBLIC_PYPI_REGISTRY_URL } from "./src/release-context.ts";
|
|
73
73
|
export { RELEASE_PUBLISH_TARGETS, RELEASE_INSTALL_MODES, ReleaseStepSelectionSchema } from "./src/release-options.ts";
|
|
74
74
|
export type { ReleasePublishTarget, ReleaseInstallMode, ReleaseStepSelection, ReleaseSelectionOptions } from "./src/release-options.ts";
|
|
75
75
|
export { SHARED_CORE_BINDING_EXPORTS } from "./src/shared-core-usage.ts";
|
|
@@ -77,6 +77,7 @@ export type { SharedCoreUsageKind, SharedCoreExportUsage } from "./src/shared-co
|
|
|
77
77
|
export { AGNOSTIC_COMPILER_OPTIONS, PACKAGE_TAG_MIXINS } from "./src/tags.ts";
|
|
78
78
|
export type { PackageTag } from "./src/tags.ts";
|
|
79
79
|
export { DBXToolsRootTsconfig } from "./src/tsconfig.ts";
|
|
80
|
+
export type { DBXToolsRootTsconfigOptions } from "./src/tsconfig.ts";
|
|
80
81
|
export type { IgnoreGroupOptions, WatchLoopOptions } from "./src/watch.ts";
|
|
81
82
|
export type { WorkspaceMutationLockOptions } from "./src/workspace-lock.ts";
|
|
82
83
|
export { VERSION_FILE, DEFAULT_VERSION } from "./src/workspace-version.ts";
|
package/package.json
CHANGED
|
@@ -30,11 +30,11 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@clack/prompts": "^1.7.0",
|
|
33
|
-
"@dbx-tools/auth": "^0.9.
|
|
34
|
-
"@dbx-tools/cli-args": "^0.9.
|
|
35
|
-
"@dbx-tools/core": "^0.9.
|
|
36
|
-
"@dbx-tools/path": "^0.9.
|
|
37
|
-
"@dbx-tools/shared-core": "^0.9.
|
|
33
|
+
"@dbx-tools/auth": "^0.9.154",
|
|
34
|
+
"@dbx-tools/cli-args": "^0.9.154",
|
|
35
|
+
"@dbx-tools/core": "^0.9.154",
|
|
36
|
+
"@dbx-tools/path": "^0.9.154",
|
|
37
|
+
"@dbx-tools/shared-core": "^0.9.154",
|
|
38
38
|
"commander": "^15.0.0",
|
|
39
39
|
"concurrently": "^10.0.3",
|
|
40
40
|
"constructs": "^10.6.0",
|
|
@@ -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.154",
|
|
60
60
|
"types": "index.ts",
|
|
61
61
|
"type": "module",
|
|
62
62
|
"exports": {
|
|
63
63
|
".": "./index.ts",
|
|
64
|
+
"./pypi-trusted-publisher-instructions": "./tasks/pypi-trusted-publisher-instructions.ts",
|
|
64
65
|
"./python-release-packaging": "./tasks/publish-python.ts",
|
|
65
66
|
"./release-packaging": "./tasks/publish-npm.ts",
|
|
66
67
|
"./package.json": "./package.json"
|
package/src/appkit-workspace.ts
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
import { existsSync } from "node:fs";
|
|
7
|
-
import { join, relative, sep } from "node:path";
|
|
8
|
-
import { Component, type Task, javascript, typescript } from "projen";
|
|
7
|
+
import { basename, join, relative, sep } from "node:path";
|
|
8
|
+
import { Component, type Task, TextFile, javascript, typescript } from "projen";
|
|
9
|
+
import { headerLines } from "./generated.ts";
|
|
9
10
|
|
|
10
11
|
/** AppKit workspace discovery and validation mode. */
|
|
11
12
|
export type AppKitWorkspaceMode = "auto" | boolean;
|
|
@@ -94,9 +95,7 @@ function validateWorkspace(options: AppKitWorkspaceOptions): void {
|
|
|
94
95
|
if (options.apps.length === 0) missing.push("an `app` package");
|
|
95
96
|
if (options.servers.length === 0) missing.push("a `server` package");
|
|
96
97
|
for (const app of options.apps) {
|
|
97
|
-
|
|
98
|
-
if (!existsSync(join(app.outdir, file))) missing.push(`${app.name}/${file}`);
|
|
99
|
-
}
|
|
98
|
+
if (!existsSync(join(app.outdir, "index.html"))) missing.push(`${app.name}/index.html`);
|
|
100
99
|
}
|
|
101
100
|
for (const server of options.servers) {
|
|
102
101
|
const entrypoint = serverEntrypoint(server);
|
|
@@ -117,11 +116,47 @@ function configureApp(project: javascript.NodeProject): void {
|
|
|
117
116
|
"@tailwindcss/vite@catalog:",
|
|
118
117
|
"tailwindcss@catalog:",
|
|
119
118
|
);
|
|
120
|
-
project.addDevDeps("vite@catalog:");
|
|
119
|
+
project.addDevDeps("@databricks/appkit@catalog:", "vite@catalog:");
|
|
120
|
+
const typesDirectory = `${basename(project.outdir)}/shared/appkit-types`;
|
|
121
|
+
new TextFile(project, "vite.config.ts", {
|
|
122
|
+
marker: true,
|
|
123
|
+
readonly: true,
|
|
124
|
+
lines: [
|
|
125
|
+
...headerLines({ tool: "projen", source: ".projenrc.ts" }),
|
|
126
|
+
'import { existsSync } from "node:fs";',
|
|
127
|
+
'import { appKitServingTypesPlugin, appKitTypesPlugin } from "@databricks/appkit";',
|
|
128
|
+
'import tailwindcss from "@tailwindcss/vite";',
|
|
129
|
+
'import { defineConfig, mergeConfig, type UserConfig } from "vite";',
|
|
130
|
+
"",
|
|
131
|
+
`const appKitTypesDirectory = ${JSON.stringify(typesDirectory)};`,
|
|
132
|
+
'const overrideUrl = new URL("./vite.config.override.ts", import.meta.url);',
|
|
133
|
+
"const override = existsSync(overrideUrl)",
|
|
134
|
+
" ? ((await import(overrideUrl.href)).default as UserConfig)",
|
|
135
|
+
" : {};",
|
|
136
|
+
"",
|
|
137
|
+
"export default defineConfig(",
|
|
138
|
+
" mergeConfig(",
|
|
139
|
+
" {",
|
|
140
|
+
" plugins: [",
|
|
141
|
+
" tailwindcss(),",
|
|
142
|
+
" appKitTypesPlugin({",
|
|
143
|
+
" outFile: `${appKitTypesDirectory}/analytics.d.ts`,",
|
|
144
|
+
" }),",
|
|
145
|
+
" appKitServingTypesPlugin({",
|
|
146
|
+
" outFile: `${appKitTypesDirectory}/serving.d.ts`,",
|
|
147
|
+
" }),",
|
|
148
|
+
" ],",
|
|
149
|
+
" },",
|
|
150
|
+
" override,",
|
|
151
|
+
" ),",
|
|
152
|
+
");",
|
|
153
|
+
],
|
|
154
|
+
});
|
|
121
155
|
|
|
122
156
|
if (!(project instanceof typescript.TypeScriptProject)) return;
|
|
123
157
|
project.tsconfig?.file.addOverride("compilerOptions.rootDir", ".");
|
|
124
158
|
project.tsconfig?.addInclude("vite.config.ts");
|
|
159
|
+
project.tsconfig?.addInclude("vite.config.override.ts");
|
|
125
160
|
project.tsconfig?.addInclude("shared/appkit-types");
|
|
126
161
|
}
|
|
127
162
|
|
package/src/bun-app.ts
CHANGED
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
*/
|
|
25
25
|
import { stringUtils } from "@dbx-tools/shared-core";
|
|
26
26
|
import { type Project, TextFile } from "projen";
|
|
27
|
+
import { header, headerLines } from "./generated.ts";
|
|
27
28
|
|
|
28
29
|
/** Unmanaged dev-server override module, merged over the generated options if present. */
|
|
29
30
|
export const BUN_DEV_OVERRIDE = "bun-dev.override.ts";
|
|
@@ -31,6 +32,8 @@ export const BUN_DEV_OVERRIDE = "bun-dev.override.ts";
|
|
|
31
32
|
export const BUN_BUILD_OVERRIDE = "bun-build.override.ts";
|
|
32
33
|
/** Unmanaged generated-script overrides excluded from formatting and linting. */
|
|
33
34
|
export const BUN_APP_OVERRIDES = [BUN_DEV_OVERRIDE, BUN_BUILD_OVERRIDE];
|
|
35
|
+
const PROJEN_HEADER = { tool: "projen", source: ".projenrc.ts" } as const;
|
|
36
|
+
const PROJEN_TS_HEADER = header(PROJEN_HEADER).trimEnd().replaceAll("\n", "\n ");
|
|
34
37
|
|
|
35
38
|
/**
|
|
36
39
|
* Root `bunfig.toml`: pin bun's HOISTED linker workspace-wide.
|
|
@@ -49,7 +52,7 @@ export class RootBunfigFile extends TextFile {
|
|
|
49
52
|
marker: false,
|
|
50
53
|
readonly: true,
|
|
51
54
|
lines: [
|
|
52
|
-
|
|
55
|
+
...headerLines({ ...PROJEN_HEADER, comment: "#" }),
|
|
53
56
|
"# Hoisted (flat) node_modules so a peer dep like @mastra/core resolves to ONE",
|
|
54
57
|
"# copy - the isolated default splits it into peer-hash variants and breaks",
|
|
55
58
|
"# identity-based singletons + cross-package types.",
|
|
@@ -76,6 +79,7 @@ export class BunfigFile extends TextFile {
|
|
|
76
79
|
marker: true,
|
|
77
80
|
readonly: true,
|
|
78
81
|
lines: [
|
|
82
|
+
...headerLines({ ...PROJEN_HEADER, comment: "#" }),
|
|
79
83
|
"# Tailwind v4 for the dev server (Bun.serve reads [serve.static] plugins).",
|
|
80
84
|
"# Production build applies the plugin via Bun.build's API in build.ts - the",
|
|
81
85
|
"# `bun build` CLI does not load bunfig plugins, so there is no [build] here.",
|
|
@@ -94,6 +98,7 @@ export class BunDevServerFile extends TextFile {
|
|
|
94
98
|
const source = (
|
|
95
99
|
// ============================================================================
|
|
96
100
|
/*ts*/`
|
|
101
|
+
${PROJEN_TS_HEADER}
|
|
97
102
|
import { existsSync } from "node:fs";
|
|
98
103
|
import index from "./index.html";
|
|
99
104
|
|
|
@@ -135,6 +140,7 @@ export class BunBuildFile extends TextFile {
|
|
|
135
140
|
String.raw
|
|
136
141
|
// ============================================================================
|
|
137
142
|
/*ts*/`
|
|
143
|
+
${PROJEN_TS_HEADER}
|
|
138
144
|
import { existsSync } from "node:fs";
|
|
139
145
|
import { cp, rm } from "node:fs/promises";
|
|
140
146
|
import { basename } from "node:path";
|
package/src/generated.ts
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { chmodSync, existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
12
12
|
|
|
13
|
-
/**
|
|
14
|
-
const MARKER = "
|
|
13
|
+
/** Tool-owned marker text shared by every supported comment style. */
|
|
14
|
+
const MARKER = "GENERATED by";
|
|
15
15
|
|
|
16
16
|
/** Python subpackage, under a package's module directory, that holds all toolchain output. */
|
|
17
17
|
export const PYTHON_GENERATED_PACKAGE = "_generated";
|
|
@@ -34,6 +34,8 @@ export function makeReadonly(file: string): void {
|
|
|
34
34
|
|
|
35
35
|
/** Inputs for the durable generated-file banner. */
|
|
36
36
|
export interface HeaderOpts {
|
|
37
|
+
/** Line-comment prefix. Defaults to `//`. */
|
|
38
|
+
readonly comment?: "//" | "#";
|
|
37
39
|
/** What produced the file, e.g. `"projen watch"`. */
|
|
38
40
|
readonly tool: string;
|
|
39
41
|
/** What the file is derived from, e.g. `"the exporting modules beside it"`. */
|
|
@@ -42,12 +44,18 @@ export interface HeaderOpts {
|
|
|
42
44
|
|
|
43
45
|
/** Build the do-not-edit banner (line comments, for TS/JS). */
|
|
44
46
|
export function header(opts: HeaderOpts): string {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
lines.push(
|
|
47
|
+
const comment = opts.comment ?? "//";
|
|
48
|
+
const lines = [`${comment} ${MARKER} ${opts.tool} - DO NOT EDIT.`];
|
|
49
|
+
if (opts.source) lines.push(`${comment} Regenerated from ${opts.source}.`);
|
|
50
|
+
lines.push(`${comment} Hand edits are overwritten on the next watch; this file is read-only.`);
|
|
48
51
|
return `${lines.join("\n")}\n`;
|
|
49
52
|
}
|
|
50
53
|
|
|
54
|
+
/** Build a generated banner as lines for Projen file components. */
|
|
55
|
+
export function headerLines(opts: HeaderOpts): string[] {
|
|
56
|
+
return header(opts).trimEnd().split("\n");
|
|
57
|
+
}
|
|
58
|
+
|
|
51
59
|
/**
|
|
52
60
|
* True if the file exists and carries the do-not-edit banner {@link header}
|
|
53
61
|
* writes - i.e. a toolchain wrote it and a hand edit would be overwritten.
|
|
@@ -61,7 +69,8 @@ export function isGenerated(file: string): boolean {
|
|
|
61
69
|
try {
|
|
62
70
|
const source = readFileSync(file, "utf8");
|
|
63
71
|
return (
|
|
64
|
-
source.startsWith(MARKER) ||
|
|
72
|
+
source.startsWith(`// ${MARKER}`) ||
|
|
73
|
+
source.startsWith(`# ${MARKER}`) ||
|
|
65
74
|
source.startsWith("// ~~ Generated by projen") ||
|
|
66
75
|
source.startsWith("# ~~ Generated by projen") ||
|
|
67
76
|
/"\/\/"\s*:\s*"~~ Generated by projen/.test(source)
|
|
@@ -78,7 +87,7 @@ export function isGenerated(file: string): boolean {
|
|
|
78
87
|
export function stampGenerated(file: string, opts: HeaderOpts): void {
|
|
79
88
|
makeWritable(file);
|
|
80
89
|
const body = readFileSync(file, "utf8");
|
|
81
|
-
const next =
|
|
90
|
+
const next = isGenerated(file) ? body : `${header(opts)}\n${body}`;
|
|
82
91
|
writeFileSync(file, next);
|
|
83
92
|
makeReadonly(file);
|
|
84
93
|
}
|
package/src/packages.ts
CHANGED
|
@@ -146,11 +146,16 @@ export class DiscoveredPackage {
|
|
|
146
146
|
|
|
147
147
|
/**
|
|
148
148
|
* Tag candidates from nesting under the discovery root: cumulative kebab-case join
|
|
149
|
-
* of every ancestor folder, excluding the leaf package folder when depth >= 2
|
|
149
|
+
* of every ancestor folder, excluding the leaf package folder when depth >= 2.
|
|
150
|
+
* Terminal `app` and `server` directories are runtime roles and replace their
|
|
151
|
+
* generic ancestor (`ui/app` -> `app`, `node/server` -> `server`).
|
|
152
|
+
* Other paths keep cumulative tags
|
|
150
153
|
* (`shared/path/coolDude/another` -> `[shared, shared-path, shared-path-cool-dude]`).
|
|
151
154
|
* Matched against `packageTagPaths` to resolve applied mixin tags.
|
|
152
155
|
*/
|
|
153
156
|
get tagCandidates(): string[] {
|
|
157
|
+
const leaf = stringUtils.toSlug(this.name);
|
|
158
|
+
if (leaf === "app" || leaf === "server") return [leaf];
|
|
154
159
|
return nestingTagsFromSegments(this.relSegments);
|
|
155
160
|
}
|
|
156
161
|
}
|
package/src/pnpm-workspace.ts
CHANGED
|
@@ -64,7 +64,7 @@ const DEFAULT_CATALOG: Catalog = {
|
|
|
64
64
|
"@types/react-dom": "^19.2.2",
|
|
65
65
|
tailwindcss: "^4.3.2",
|
|
66
66
|
"@tailwindcss/vite": "^4.3.2",
|
|
67
|
-
vite: "
|
|
67
|
+
vite: "npm:rolldown-vite@7.1.14",
|
|
68
68
|
"@vitejs/plugin-react": "^5.1.1",
|
|
69
69
|
// Tailwind v4 compiler for bun's dev server + `Bun.build` (the `app` tag).
|
|
70
70
|
"bun-plugin-tailwind": "^0.1.2",
|
package/src/project-js.ts
CHANGED
|
@@ -40,7 +40,7 @@ import { applyCompiledPublish } from "./publish.ts";
|
|
|
40
40
|
import { generatePythonNodeBindings } from "./python-node-bindings.ts";
|
|
41
41
|
import { DBXToolsRelease, type ReleaseDocsOptions } from "./release.ts";
|
|
42
42
|
import { AGNOSTIC_COMPILER_OPTIONS, PACKAGE_TAG_MIXINS, type PackageTag } from "./tags.ts";
|
|
43
|
-
import { DBXToolsRootTsconfig } from "./tsconfig.ts";
|
|
43
|
+
import { DBXToolsRootTsconfig, type DBXToolsRootTsconfigOptions } from "./tsconfig.ts";
|
|
44
44
|
import { configureVsCode } from "./vscode.ts";
|
|
45
45
|
import { readWorkspaceVersion, syncWorkspaceManifestVersion } from "./workspace-version.ts";
|
|
46
46
|
|
|
@@ -140,7 +140,15 @@ function repositoryOptions(
|
|
|
140
140
|
options: DBXToolsJavaScriptProjectOptions,
|
|
141
141
|
): Pick<javascript.NodeProjectOptions, "repository" | "repositoryDirectory"> {
|
|
142
142
|
const workspaceRoot = resolve(options.parent?.root.outdir ?? options.outdir ?? process.cwd());
|
|
143
|
-
const
|
|
143
|
+
const existingRepository = readPackageManifest(workspaceRoot)?.repository;
|
|
144
|
+
const existingRepositoryUrl =
|
|
145
|
+
typeof existingRepository === "string"
|
|
146
|
+
? existingRepository
|
|
147
|
+
: object.isRecord(existingRepository)
|
|
148
|
+
? stringUtils.trimToNull(existingRepository.url)
|
|
149
|
+
: undefined;
|
|
150
|
+
const repository =
|
|
151
|
+
options.repository ?? projectUtils.repositoryUrl(workspaceRoot, "npm") ?? existingRepositoryUrl;
|
|
144
152
|
const projectRoot = options.parent
|
|
145
153
|
? resolve(options.parent.outdir, options.outdir ?? ".")
|
|
146
154
|
: resolve(options.outdir ?? process.cwd());
|
|
@@ -623,6 +631,8 @@ export type DBXToolsJavaScriptProjectOptions = CommonProjectOptions &
|
|
|
623
631
|
* a list = only those tags.
|
|
624
632
|
*/
|
|
625
633
|
readonly defaultTagMixins?: false | PackageTag[];
|
|
634
|
+
/** Overrides for the generated repository-root TypeScript program. */
|
|
635
|
+
readonly rootTsconfig?: DBXToolsRootTsconfigOptions;
|
|
626
636
|
/**
|
|
627
637
|
* Apply AppKit conventions to `app` and `server` packages. Defaults to
|
|
628
638
|
* `auto`; `true` also validates the required package roles and entry files.
|
|
@@ -1065,6 +1075,7 @@ class PrettierIgnoreGenerated extends Component {
|
|
|
1065
1075
|
if (!(sub instanceof DBXToolsTypeScriptProject)) continue;
|
|
1066
1076
|
const rel = toPosix(relative(rootAbs, sub.outdir));
|
|
1067
1077
|
prettier.addIgnorePattern(`${rel}/index.ts`);
|
|
1078
|
+
prettier.addIgnorePattern(`${rel}/src/**/index.ts`);
|
|
1068
1079
|
for (const module of codegenModulePaths(sub.dbxToolsConfig.codegenInputs)) {
|
|
1069
1080
|
prettier.addIgnorePattern(`${rel}/${module}`);
|
|
1070
1081
|
}
|
|
@@ -1236,7 +1247,7 @@ function initProject(
|
|
|
1236
1247
|
}
|
|
1237
1248
|
|
|
1238
1249
|
if (project instanceof DBXToolsNodeProject) {
|
|
1239
|
-
project.rootTsconfig = new DBXToolsRootTsconfig(project);
|
|
1250
|
+
project.rootTsconfig = new DBXToolsRootTsconfig(project, options.rootTsconfig);
|
|
1240
1251
|
}
|
|
1241
1252
|
const projenrc = new typescript.ProjenrcTs(project, {
|
|
1242
1253
|
runner: typescript.TypeScriptRunner.nodejs(),
|
|
@@ -1264,7 +1275,9 @@ function initProject(
|
|
|
1264
1275
|
// lockfiles, so the CI-named install task must resolve from manifests as well.
|
|
1265
1276
|
project.package.installCiTask.reset("bun install");
|
|
1266
1277
|
configureRootBunRuntime(project);
|
|
1267
|
-
const roots =
|
|
1278
|
+
const roots =
|
|
1279
|
+
options.packageRoots ??
|
|
1280
|
+
(existsSync(resolve(project.outdir, "packages/js")) ? ["packages/js"] : DEFAULT_PACKAGE_ROOTS);
|
|
1268
1281
|
project.dbxToolsConfig.packageRoots = [...roots];
|
|
1269
1282
|
if (options.syncResynthPaths?.length) {
|
|
1270
1283
|
project.dbxToolsConfig.syncResynthPaths = [...options.syncResynthPaths];
|
|
@@ -1289,6 +1302,9 @@ function initProject(
|
|
|
1289
1302
|
project.gitignore.addPatterns(
|
|
1290
1303
|
...[...ignore.ignorePatterns({ test: false, dot: false, lock: true })],
|
|
1291
1304
|
);
|
|
1305
|
+
// The generic compiled-output pattern `**/lib/**` must not hide authored
|
|
1306
|
+
// modules beneath a package's `src/lib/`.
|
|
1307
|
+
project.gitignore.include("**/src/lib/**");
|
|
1292
1308
|
// What the dot group was actually earning here, named explicitly: secrets and
|
|
1293
1309
|
// local editor state. Both ignore CONTENTS (`.idea/*`) rather than the
|
|
1294
1310
|
// directory, so a later `!` negation can still reach a file inside.
|
package/src/project-py.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/** Reusable uv workspace generation for Python packages hosted in a projen tree. */
|
|
2
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
2
4
|
import * as projectUtils from "@dbx-tools/core/project-utils";
|
|
3
5
|
import { stringUtils, type OneOrMany } from "@dbx-tools/shared-core";
|
|
4
6
|
import { Component, License, TextFile, type Project, github, javascript, python } from "projen";
|
|
5
7
|
import { JobPermission, type Job, type JobStep } from "projen/lib/github/workflows-model";
|
|
6
|
-
import { PYTHON_GENERATED_PACKAGE } from "./generated.ts";
|
|
8
|
+
import { header, PYTHON_GENERATED_PACKAGE } from "./generated.ts";
|
|
7
9
|
import { LICENSE, projectRepositoryUrl, taskCommand } from "./project-js.ts";
|
|
8
10
|
import { isDBXToolsJavaScriptProject } from "./project-predicate.ts";
|
|
9
11
|
import type { DBXToolsProject, DBXToolsProjectOptions } from "./project.ts";
|
|
@@ -17,6 +19,7 @@ import {
|
|
|
17
19
|
PythonNodeRuntime,
|
|
18
20
|
} from "./python-node-runtime.ts";
|
|
19
21
|
import {
|
|
22
|
+
refreshPublicationVerification,
|
|
20
23
|
refreshReleaseDocsDependencies,
|
|
21
24
|
releaseCondition,
|
|
22
25
|
releaseBuildSteps,
|
|
@@ -107,7 +110,10 @@ interface PythonPublication {
|
|
|
107
110
|
|
|
108
111
|
/** Options for {@link DBXToolsPythonWorkspace}. */
|
|
109
112
|
export interface DBXToolsPythonWorkspaceOptions {
|
|
110
|
-
|
|
113
|
+
/** Package-specific options merged over packages discovered under the Python root. */
|
|
114
|
+
readonly packages?: readonly PythonPackageOptions[];
|
|
115
|
+
/** Discover direct child directories containing `src/`. Defaults to true. */
|
|
116
|
+
readonly discoverPackages?: boolean;
|
|
111
117
|
readonly repository?: PythonRepositoryOptions;
|
|
112
118
|
/** Repository-relative Python package root. Defaults to `packages/py`. */
|
|
113
119
|
readonly root?: string;
|
|
@@ -214,6 +220,15 @@ export function pythonModuleName(scope: string, directory: string): string {
|
|
|
214
220
|
return [scope, ...directory.split("/")].map((part) => part.replaceAll("-", "_")).join(".");
|
|
215
221
|
}
|
|
216
222
|
|
|
223
|
+
/** Discover direct Python workspace members by their conventional source directory. */
|
|
224
|
+
function discoverPythonPackages(root: string): string[] {
|
|
225
|
+
if (!existsSync(root)) return [];
|
|
226
|
+
return readdirSync(root, { withFileTypes: true })
|
|
227
|
+
.filter((entry) => entry.isDirectory() && existsSync(resolve(root, entry.name, "src")))
|
|
228
|
+
.map((entry) => entry.name)
|
|
229
|
+
.sort();
|
|
230
|
+
}
|
|
231
|
+
|
|
217
232
|
/** A Python package implemented with projen's `PythonProject` and uv backend. */
|
|
218
233
|
export class DBXToolsPythonProject extends python.PythonProject implements DBXToolsProject {
|
|
219
234
|
readonly language = "python" as const;
|
|
@@ -349,11 +364,30 @@ export class DBXToolsPythonWorkspace extends Component {
|
|
|
349
364
|
ref: options.repository?.ref ?? "main",
|
|
350
365
|
root: options.root ?? options.repository?.root ?? "packages/py",
|
|
351
366
|
};
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
367
|
+
const configuredPackages = new Map((options.packages ?? []).map((pkg) => [pkg.directory, pkg]));
|
|
368
|
+
const discoveredDirectories =
|
|
369
|
+
options.discoverPackages === false
|
|
370
|
+
? []
|
|
371
|
+
: discoverPythonPackages(resolve(project.outdir, this.repository.root));
|
|
372
|
+
const directories = [
|
|
373
|
+
...configuredPackages.keys(),
|
|
374
|
+
...discoveredDirectories.filter((directory) => !configuredPackages.has(directory)),
|
|
375
|
+
];
|
|
376
|
+
const packageIdentities: ResolvedPythonPackageOptions[] = directories.map((directory) => {
|
|
377
|
+
const pkg = configuredPackages.get(directory);
|
|
378
|
+
return {
|
|
379
|
+
directory,
|
|
380
|
+
description: `${scope} ${directory} Python package.`,
|
|
381
|
+
...pkg,
|
|
382
|
+
name: pkg?.name ?? `${scope}-${stringUtils.toSlug(directory)}`,
|
|
383
|
+
module: pkg?.module ?? pythonModuleName(scope, directory),
|
|
384
|
+
};
|
|
385
|
+
});
|
|
386
|
+
if (packageIdentities.length === 0) {
|
|
387
|
+
throw new Error(
|
|
388
|
+
`No Python packages were configured or discovered under ${this.repository.root}`,
|
|
389
|
+
);
|
|
390
|
+
}
|
|
357
391
|
const configuredNodeRuntimes = packageIdentities.filter(({ nodeRuntime }) => nodeRuntime);
|
|
358
392
|
if (configuredNodeRuntimes.length > 1) {
|
|
359
393
|
throw new Error("A Python workspace can configure only one shared Node runtime package");
|
|
@@ -672,6 +706,7 @@ export class DBXToolsPythonWorkspace extends Component {
|
|
|
672
706
|
],
|
|
673
707
|
});
|
|
674
708
|
}
|
|
709
|
+
refreshPublicationVerification(project);
|
|
675
710
|
refreshReleaseDocsDependencies(project);
|
|
676
711
|
}
|
|
677
712
|
|
|
@@ -760,26 +795,24 @@ export class DBXToolsPythonWorkspace extends Component {
|
|
|
760
795
|
}),
|
|
761
796
|
];
|
|
762
797
|
const helper = ".projen/pypi-trusted-publisher-instructions.mjs";
|
|
798
|
+
const helperHeader = header({ tool: "projen", source: ".projenrc.ts" })
|
|
799
|
+
.trimEnd()
|
|
800
|
+
.replaceAll("\n", "\n ");
|
|
763
801
|
new TextFile(project.root, helper, {
|
|
802
|
+
marker: true,
|
|
803
|
+
readonly: true,
|
|
764
804
|
// prettier-ignore
|
|
765
805
|
lines: stringUtils.dedent(
|
|
766
806
|
// ============================================================================
|
|
767
807
|
/*js*/`
|
|
768
808
|
#!/usr/bin/env bun
|
|
769
|
-
|
|
809
|
+
${helperHeader}
|
|
810
|
+
import { printPypiTrustedPublisherInstructions } from "@dbx-tools/projen/pypi-trusted-publisher-instructions";
|
|
770
811
|
|
|
771
|
-
|
|
772
|
-
|
|
812
|
+
await printPypiTrustedPublisherInstructions({
|
|
813
|
+
beforeAuthentication: ${JSON.stringify(linesBeforeAuthentication)},
|
|
814
|
+
afterAuthentication: ${JSON.stringify(linesAfterAuthentication)},
|
|
773
815
|
});
|
|
774
|
-
const beforeAuthentication = ${JSON.stringify(linesBeforeAuthentication)};
|
|
775
|
-
const afterAuthentication = ${JSON.stringify(linesAfterAuthentication)};
|
|
776
|
-
const authentication = values.secretFile
|
|
777
|
-
? [
|
|
778
|
-
\`- If browser authentication is required, read credentials from \${values.secretFile} directly into the browser without printing, logging, or exposing secret values.\`,
|
|
779
|
-
\`- If \${values.secretFile} is absent, invalid, or insufficient for authentication, pause and ask the user to complete authentication.\`,
|
|
780
|
-
]
|
|
781
|
-
: ["- If browser authentication is required, pause and ask the user to complete authentication."];
|
|
782
|
-
process.stdout.write(\`\${[...beforeAuthentication, ...authentication, ...afterAuthentication].join("\\n").trimEnd()}\\n\`);
|
|
783
816
|
`
|
|
784
817
|
// ============================================================================
|
|
785
818
|
).split("\n"),
|
package/src/release-context.ts
CHANGED
|
@@ -3,3 +3,7 @@
|
|
|
3
3
|
export const RELEASE_VERSION = "${{ needs.build-release.outputs.release_version }}";
|
|
4
4
|
/** Generated GitHub Actions workflow triggered by release tags. */
|
|
5
5
|
export const RELEASE_WORKFLOW_FILE = "release.yml";
|
|
6
|
+
/** Public npm registry queried after npm publication completes. */
|
|
7
|
+
export const PUBLIC_NPM_REGISTRY_URL = "https://registry.npmjs.org";
|
|
8
|
+
/** Public PyPI JSON API queried after Python publication completes. */
|
|
9
|
+
export const PUBLIC_PYPI_REGISTRY_URL = "https://pypi.org/pypi";
|