@dbx-tools/projen 0.9.154 → 0.9.157
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 +36 -47
- package/index.ts +42 -20
- package/package.json +8 -8
- package/src/_task-command.ts +7 -7
- package/src/appkit-workspace.ts +278 -104
- package/src/barrels.ts +147 -259
- package/src/bun-app.ts +54 -10
- package/src/clean.ts +6 -6
- package/src/engine-root.ts +4 -4
- package/src/mixin.ts +0 -2
- package/src/module-exports.ts +4 -5
- package/src/npm-packaging.ts +128 -0
- package/src/npm-release.ts +200 -0
- package/src/packages.ts +11 -11
- package/src/project-js.ts +209 -103
- package/src/project-predicate.ts +5 -12
- package/src/project-py.ts +6 -6
- package/src/project.ts +7 -14
- package/src/publish.ts +3 -3
- package/src/python-node-bindings.ts +16 -12
- package/src/python-packaging.ts +177 -0
- package/src/release-options.ts +2 -2
- package/src/release.ts +3 -3
- package/src/shared-core-usage.ts +17 -4
- package/src/tags.ts +10 -9
- package/src/watch.ts +7 -9
- package/src/workspace-lock.ts +2 -2
- package/src/workspace-manifest.ts +2 -2
- package/tasks/appkit-deploy.ts +35 -15
- package/tasks/barrels.ts +6 -9
- package/tasks/bump.ts +1 -1
- package/tasks/clean.ts +3 -3
- package/tasks/cli.ts +3 -3
- package/tasks/compile-workspace.ts +8 -8
- package/tasks/dev-watch.ts +11 -11
- package/tasks/local-publish.ts +6 -6
- package/tasks/publish-npm.ts +11 -319
- package/tasks/publish-python.ts +2 -180
- package/tasks/publish.ts +6 -11
- package/tasks/python-node-bindings.ts +2 -2
- package/tasks/python-registry.ts +6 -6
- package/tasks/python-sync.ts +2 -3
- package/tasks/release-version.ts +1 -1
- package/tasks/release.ts +11 -11
- package/tasks/sync.ts +2 -2
- package/tasks/test-workspace.ts +9 -9
- package/tasks/verify-publications.ts +9 -5
- package/tasks/version-check.ts +5 -5
- /package/{tasks → src}/python-release.ts +0 -0
package/src/project-js.ts
CHANGED
|
@@ -6,17 +6,30 @@
|
|
|
6
6
|
* {@link DBXToolsNodeProject} (monorepo root) and {@link DBXToolsTypeScriptProject}
|
|
7
7
|
* (a package, or a standalone compiling root) both implement {@link DBXToolsJavaScriptProject}.
|
|
8
8
|
*/
|
|
9
|
-
import { existsSync
|
|
9
|
+
import { existsSync } from "node:fs";
|
|
10
10
|
import { dirname, join, relative, resolve } from "node:path";
|
|
11
|
-
import
|
|
12
|
-
import { ignore,
|
|
13
|
-
import {
|
|
11
|
+
import { repositoryUrl } from "@dbx-tools/core";
|
|
12
|
+
import { ignore, findFilesSync, toPathMatcher } from "@dbx-tools/path";
|
|
13
|
+
import {
|
|
14
|
+
type OneOrMany,
|
|
15
|
+
isRecord,
|
|
16
|
+
sequence,
|
|
17
|
+
toSlug,
|
|
18
|
+
tokenize,
|
|
19
|
+
trimToNull,
|
|
20
|
+
dedent,
|
|
21
|
+
} from "@dbx-tools/shared-core";
|
|
14
22
|
import { type IConstruct } from "constructs";
|
|
15
23
|
import { Component, LogLevel, Project, type Task, TextFile, javascript, typescript } from "projen";
|
|
16
24
|
import { JobPermission, type JobStep } from "projen/lib/github/workflows-model";
|
|
17
25
|
import type { ReleaseProjectOptions } from "projen/lib/release";
|
|
18
|
-
import {
|
|
19
|
-
|
|
26
|
+
import {
|
|
27
|
+
AppKitWorkspace,
|
|
28
|
+
isAppKitApplication,
|
|
29
|
+
type AppKitBundleOptions,
|
|
30
|
+
type AppKitWorkspaceMode,
|
|
31
|
+
} from "./appkit-workspace.ts";
|
|
32
|
+
import { folderBarrelFile, generateBarrels } from "./barrels.ts";
|
|
20
33
|
|
|
21
34
|
import { BUN_APP_OVERRIDES, RootBunfigFile } from "./bun-app.ts";
|
|
22
35
|
import { BUN_VERSION, BUN_VERSION_CHECK_SCRIPT, MINIMUM_BUN_VERSION } from "./bun-workflow.ts";
|
|
@@ -27,7 +40,7 @@ import { resolvePkgRoot } from "./engine-root.ts";
|
|
|
27
40
|
import * as mixin from "./mixin.ts";
|
|
28
41
|
import {
|
|
29
42
|
DEFAULT_PACKAGE_ROOTS,
|
|
30
|
-
|
|
43
|
+
DiscoveredPackage,
|
|
31
44
|
projectName,
|
|
32
45
|
readPackageManifest,
|
|
33
46
|
scanPackages,
|
|
@@ -113,10 +126,10 @@ export class PackageIdentifier {
|
|
|
113
126
|
*/
|
|
114
127
|
static of(...names: OneOrMany<string>): PackageIdentifier {
|
|
115
128
|
const segments = names.flatMap((part) => part.split("/")).filter(Boolean);
|
|
116
|
-
const scope = segments.length ?
|
|
129
|
+
const scope = segments.length ? toSlug(segments[0]!) : "";
|
|
117
130
|
const nameParts = [
|
|
118
131
|
scope,
|
|
119
|
-
...segments.slice(1).flatMap((segment) => [...
|
|
132
|
+
...segments.slice(1).flatMap((segment) => [...tokenize(segment)]),
|
|
120
133
|
].filter(Boolean);
|
|
121
134
|
if (!nameParts.length) throw new Error(`Invalid name: ${names.join(", ")}`);
|
|
122
135
|
if (nameParts.length === 1) return new PackageIdentifier(undefined, nameParts[0]!);
|
|
@@ -144,11 +157,11 @@ function repositoryOptions(
|
|
|
144
157
|
const existingRepositoryUrl =
|
|
145
158
|
typeof existingRepository === "string"
|
|
146
159
|
? existingRepository
|
|
147
|
-
:
|
|
148
|
-
?
|
|
160
|
+
: isRecord(existingRepository)
|
|
161
|
+
? trimToNull(existingRepository.url)
|
|
149
162
|
: undefined;
|
|
150
163
|
const repository =
|
|
151
|
-
options.repository ??
|
|
164
|
+
options.repository ?? repositoryUrl(workspaceRoot, "npm") ?? existingRepositoryUrl;
|
|
152
165
|
const projectRoot = options.parent
|
|
153
166
|
? resolve(options.parent.outdir, options.outdir ?? ".")
|
|
154
167
|
: resolve(options.outdir ?? process.cwd());
|
|
@@ -239,7 +252,7 @@ function dependencyName(spec: string): string {
|
|
|
239
252
|
/** Add an optional peer and make it available while developing the package. */
|
|
240
253
|
export function addOptionalPeer(pkg: javascript.NodeProject, spec: string): void {
|
|
241
254
|
const name = dependencyName(spec);
|
|
242
|
-
const current =
|
|
255
|
+
const current = isRecord(pkg.package.manifest.peerDependenciesMeta)
|
|
243
256
|
? pkg.package.manifest.peerDependenciesMeta
|
|
244
257
|
: {};
|
|
245
258
|
pkg.addPeerDeps(spec);
|
|
@@ -256,8 +269,8 @@ export function projectRepositoryUrl(project: javascript.NodeProject): string |
|
|
|
256
269
|
const configured =
|
|
257
270
|
typeof repository === "string"
|
|
258
271
|
? repository
|
|
259
|
-
:
|
|
260
|
-
? (
|
|
272
|
+
: isRecord(repository)
|
|
273
|
+
? (trimToNull(repository.url) ?? undefined)
|
|
261
274
|
: undefined;
|
|
262
275
|
return configured?.replace(/^git\+/, "").replace(/\.git$/, "");
|
|
263
276
|
}
|
|
@@ -285,47 +298,45 @@ export function projectReleaseBranch(project: DBXToolsJavaScriptProject): string
|
|
|
285
298
|
*/
|
|
286
299
|
export function addPackageFiles(pkg: javascript.NodeProject, ...entries: string[]): void {
|
|
287
300
|
const current = (pkg.package.manifest.files ?? []) as string[];
|
|
288
|
-
pkg.package.addField("files", [...
|
|
301
|
+
pkg.package.addField("files", [...sequence(current, entries).distinct()]);
|
|
289
302
|
}
|
|
290
303
|
|
|
291
|
-
/**
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
* This widens no API surface: the root `index.ts` barrel already re-exports every
|
|
297
|
-
* non-`_` module, so those names are public through `.` either way - the subpaths
|
|
298
|
-
* just add a narrower import path. Deriving the map is what lets a tag carry the
|
|
299
|
-
* whole export layout, instead of each package hand-listing its own modules.
|
|
300
|
-
* The wildcard resolves generated nested barrels at every depth while exact
|
|
301
|
-
* exports continue to win for hand-authored entrypoints and assets.
|
|
302
|
-
*/
|
|
303
|
-
export function srcModuleExports(pkg: javascript.NodeProject): Record<string, string> {
|
|
304
|
+
/** Generate folder-only TypeScript subpaths plus public CSS asset subpaths. */
|
|
305
|
+
export function srcFolderExports(
|
|
306
|
+
pkg: javascript.NodeProject,
|
|
307
|
+
additionalModules: readonly string[] = [],
|
|
308
|
+
): Record<string, string> {
|
|
304
309
|
const srcDir = join(pkg.outdir, "src");
|
|
305
310
|
if (!existsSync(srcDir)) return {};
|
|
306
311
|
|
|
312
|
+
const discovered = [...findFilesSync("**/*", { cwd: srcDir })].map(toPosix);
|
|
313
|
+
const modules = new Set([
|
|
314
|
+
...discovered.filter(
|
|
315
|
+
(file) =>
|
|
316
|
+
/\.(?:ts|tsx)$/.test(file) &&
|
|
317
|
+
!file.endsWith(".d.ts") &&
|
|
318
|
+
!/(^|\/)_/.test(file) &&
|
|
319
|
+
!/\.(?:test|spec)\./.test(file) &&
|
|
320
|
+
!/(^|\/)index\.(?:ts|tsx)$/.test(file),
|
|
321
|
+
),
|
|
322
|
+
...additionalModules
|
|
323
|
+
.map((file) => toPosix(file).replace(/^src\//, ""))
|
|
324
|
+
.filter((file) => /\.(?:ts|tsx)$/.test(file) && !/(^|\/)_/.test(file)),
|
|
325
|
+
]);
|
|
326
|
+
const directories = new Set<string>();
|
|
327
|
+
for (const module of modules) {
|
|
328
|
+
const directory = dirname(module);
|
|
329
|
+
if (directory !== ".") directories.add(directory);
|
|
330
|
+
}
|
|
331
|
+
|
|
307
332
|
const exports: Record<string, string> = {};
|
|
308
|
-
for (const
|
|
309
|
-
|
|
310
|
-
)) {
|
|
311
|
-
if (entry.name.startsWith("_")) continue;
|
|
312
|
-
if (entry.isFile() && /\.(?:ts|tsx)$/.test(entry.name) && !entry.name.endsWith(".d.ts")) {
|
|
313
|
-
exports[`./${entry.name.replace(/\.tsx?$/, "")}`] = `./src/${entry.name}`;
|
|
314
|
-
continue;
|
|
315
|
-
}
|
|
316
|
-
if (entry.isFile() && entry.name.endsWith(".css")) {
|
|
317
|
-
exports[`./${entry.name}`] = `./src/${entry.name}`;
|
|
318
|
-
continue;
|
|
319
|
-
}
|
|
320
|
-
if (!entry.isDirectory()) continue;
|
|
321
|
-
const index = ["index.ts", "index.tsx"].find((file) =>
|
|
322
|
-
existsSync(join(srcDir, entry.name, file)),
|
|
323
|
-
);
|
|
324
|
-
if (index && !isGeneratedBarrel(join(srcDir, entry.name, index))) {
|
|
325
|
-
exports[`./${entry.name}`] = `./src/${entry.name}/${index}`;
|
|
326
|
-
}
|
|
333
|
+
for (const directory of [...directories].sort((left, right) => left.localeCompare(right))) {
|
|
334
|
+
exports[`./${directory}`] = `./${folderBarrelFile(directory)}`;
|
|
327
335
|
}
|
|
328
|
-
|
|
336
|
+
for (const file of discovered.filter((file) => file.endsWith(".css") && !/(^|\/)_/.test(file))) {
|
|
337
|
+
exports[`./${file}`] = `./src/${file}`;
|
|
338
|
+
}
|
|
339
|
+
return exports;
|
|
329
340
|
}
|
|
330
341
|
|
|
331
342
|
/**
|
|
@@ -377,8 +388,6 @@ const PRETTIER_SETTINGS: javascript.PrettierSettings = {
|
|
|
377
388
|
endOfLine: javascript.EndOfLine.LF,
|
|
378
389
|
};
|
|
379
390
|
|
|
380
|
-
export { PROJEN_VERSION } from "./projen-version.ts";
|
|
381
|
-
|
|
382
391
|
/** SPDX license shared by generated JavaScript and Python packages. */
|
|
383
392
|
export const LICENSE = "Apache-2.0";
|
|
384
393
|
|
|
@@ -520,10 +529,8 @@ const DEV_DEPS_ROOT: string[] = [
|
|
|
520
529
|
|
|
521
530
|
function configureRootBunRuntime(project: DBXToolsJavaScriptProject): void {
|
|
522
531
|
new TextFile(project, BUN_VERSION_CHECK_SCRIPT, {
|
|
523
|
-
lines:
|
|
524
|
-
|
|
525
|
-
// ============================================================================
|
|
526
|
-
/*ts*/ `
|
|
532
|
+
lines: dedent(// ============================================================================
|
|
533
|
+
/*ts*/ `
|
|
527
534
|
/** Minimum Bun version accepted by this workspace. */
|
|
528
535
|
const MINIMUM_VERSION = ${JSON.stringify(MINIMUM_BUN_VERSION)};
|
|
529
536
|
|
|
@@ -550,10 +557,8 @@ function configureRootBunRuntime(project: DBXToolsJavaScriptProject): void {
|
|
|
550
557
|
\`Bun \${MINIMUM_VERSION} or newer is required; running \${Bun.version}. Use "bun run bun" to select the workspace-local Bun binary.\`,
|
|
551
558
|
);
|
|
552
559
|
}
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
)
|
|
556
|
-
.split("\n"),
|
|
560
|
+
`).split("\n"),
|
|
561
|
+
// ============================================================================
|
|
557
562
|
});
|
|
558
563
|
const check = `bun ${BUN_VERSION_CHECK_SCRIPT}`;
|
|
559
564
|
project.package.installTask.prependExec(check);
|
|
@@ -638,6 +643,13 @@ export type DBXToolsJavaScriptProjectOptions = CommonProjectOptions &
|
|
|
638
643
|
* `auto`; `true` also validates the required package roles and entry files.
|
|
639
644
|
*/
|
|
640
645
|
readonly appkit?: AppKitWorkspaceMode;
|
|
646
|
+
/**
|
|
647
|
+
* One Bun/AppKit application root. Defaults to `packages/app` when that
|
|
648
|
+
* directory exists. Set false to disable application-root discovery.
|
|
649
|
+
*/
|
|
650
|
+
readonly appRoot?: string | false;
|
|
651
|
+
/** Minimal generated bundle settings, or false for an application-owned bundle. */
|
|
652
|
+
readonly appBundle?: false | AppKitBundleOptions;
|
|
641
653
|
/**
|
|
642
654
|
* Extra repo-root paths that trigger a full re-synth during `sync --watch`
|
|
643
655
|
* (alongside `.projenrc.ts`). Repo-relative, e.g. `".example.projenrc.ts"`.
|
|
@@ -877,22 +889,22 @@ export class DBXToolsTypeScriptProject
|
|
|
877
889
|
// instead of failing, so authoring a React component would otherwise need
|
|
878
890
|
// per-package tsconfig config to be compiled at all.
|
|
879
891
|
this.tsconfig?.addInclude("src/**/*.tsx");
|
|
892
|
+
this.tsconfig?.addInclude("index.*.ts");
|
|
880
893
|
this.dbxToolsConfig = new DBXToolsConfig(this, options);
|
|
881
894
|
// Source-first entry: point the package at its package-ROOT `index.ts` barrel
|
|
882
895
|
// so packages resolve each other's `@scope/pkg` imports to source.
|
|
883
896
|
this.package.addField("type", "module");
|
|
884
897
|
this.package.addField("exports", {
|
|
885
898
|
".": "./index.ts",
|
|
886
|
-
...
|
|
899
|
+
...srcFolderExports(this),
|
|
887
900
|
"./package.json": "./package.json",
|
|
888
901
|
});
|
|
889
902
|
this.package.addVersion(readWorkspaceVersion(this.root.outdir));
|
|
890
|
-
addPackageFiles(this, "index
|
|
891
|
-
if (existsSync(join(this.outdir, "exports.ts"))) addPackageFiles(this, "exports.ts");
|
|
903
|
+
addPackageFiles(this, "index*.ts", "src");
|
|
892
904
|
// `bun test` intercepts `node:test` (the suites keep using node:test) and
|
|
893
905
|
// runs it with bun's own fast runner. The native no-tests option is portable
|
|
894
906
|
// across Windows and Unix and lets packages without tests remain a no-op.
|
|
895
|
-
this.testTask.exec(
|
|
907
|
+
this.testTask.exec(`bun test ${this.testdir} --pass-with-no-tests`);
|
|
896
908
|
initProject(this, options);
|
|
897
909
|
}
|
|
898
910
|
|
|
@@ -945,19 +957,11 @@ class GeneratedSource extends Component {
|
|
|
945
957
|
}
|
|
946
958
|
if (modules.length === 0) continue;
|
|
947
959
|
const current = (subproject.package.manifest.exports ?? {}) as Record<string, unknown>;
|
|
948
|
-
const
|
|
949
|
-
modules.map((module) => [`./${module.slice("src/".length, -".ts".length)}`, `./${module}`]),
|
|
950
|
-
);
|
|
951
|
-
const nestedExport = current["./*"];
|
|
960
|
+
const { "./package.json": packageJson, ...withoutPackageJson } = current;
|
|
952
961
|
subproject.package.addField("exports", {
|
|
953
|
-
...
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
),
|
|
957
|
-
),
|
|
958
|
-
...sourceExports,
|
|
959
|
-
...(nestedExport ? { "./*": nestedExport } : {}),
|
|
960
|
-
...(current["./package.json"] ? { "./package.json": current["./package.json"] } : {}),
|
|
962
|
+
...withoutPackageJson,
|
|
963
|
+
...srcFolderExports(subproject, modules),
|
|
964
|
+
...(packageJson ? { "./package.json": packageJson } : {}),
|
|
961
965
|
});
|
|
962
966
|
}
|
|
963
967
|
}
|
|
@@ -1058,6 +1062,62 @@ class EslintIgnoreCodegen extends Component {
|
|
|
1058
1062
|
}
|
|
1059
1063
|
}
|
|
1060
1064
|
|
|
1065
|
+
/** Require package implementation files to import their own modules relatively. */
|
|
1066
|
+
class EslintWorkspaceImports extends Component {
|
|
1067
|
+
private readonly configured = new Set<string>();
|
|
1068
|
+
|
|
1069
|
+
public override preSynthesize(): void {
|
|
1070
|
+
const eslint = javascript.Eslint.of(this.project);
|
|
1071
|
+
if (!eslint) return;
|
|
1072
|
+
const root = this.project as DBXToolsNodeProject | DBXToolsTypeScriptProject;
|
|
1073
|
+
const rootAbs = resolve(root.outdir);
|
|
1074
|
+
const configure = (directory: string, packageName: string): void => {
|
|
1075
|
+
const rel = toPosix(relative(rootAbs, directory));
|
|
1076
|
+
const key = `${rel}:${packageName}`;
|
|
1077
|
+
if (this.configured.has(key)) return;
|
|
1078
|
+
this.configured.add(key);
|
|
1079
|
+
eslint.addOverride({
|
|
1080
|
+
files: [`${rel ? `${rel}/` : ""}**/*.{ts,tsx}`],
|
|
1081
|
+
rules: {
|
|
1082
|
+
"no-restricted-imports": [
|
|
1083
|
+
"error",
|
|
1084
|
+
{
|
|
1085
|
+
patterns: [
|
|
1086
|
+
{
|
|
1087
|
+
group: [packageName, `${packageName}/**`],
|
|
1088
|
+
message: "Use relative imports within this package.",
|
|
1089
|
+
},
|
|
1090
|
+
],
|
|
1091
|
+
},
|
|
1092
|
+
],
|
|
1093
|
+
},
|
|
1094
|
+
});
|
|
1095
|
+
};
|
|
1096
|
+
|
|
1097
|
+
const members = root.node
|
|
1098
|
+
.findAll()
|
|
1099
|
+
.filter(
|
|
1100
|
+
(candidate): candidate is DBXToolsNodeProject | DBXToolsTypeScriptProject =>
|
|
1101
|
+
candidate !== root &&
|
|
1102
|
+
(candidate instanceof DBXToolsNodeProject ||
|
|
1103
|
+
candidate instanceof DBXToolsTypeScriptProject),
|
|
1104
|
+
);
|
|
1105
|
+
for (const member of members) {
|
|
1106
|
+
configure(member.outdir, member.package.packageName);
|
|
1107
|
+
}
|
|
1108
|
+
if (members.length === 0 && root.extraWorkspaceMembers.length === 0) {
|
|
1109
|
+
configure(root.outdir, root.package.packageName);
|
|
1110
|
+
}
|
|
1111
|
+
for (const member of root.extraWorkspaceMembers) {
|
|
1112
|
+
const directory = resolve(rootAbs, member);
|
|
1113
|
+
const packageName = readPackageManifest(directory)?.name;
|
|
1114
|
+
if (typeof packageName === "string" && packageName.trim()) {
|
|
1115
|
+
configure(directory, packageName);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1061
1121
|
/**
|
|
1062
1122
|
* Keep generated package barrels and codegen modules out of root formatting.
|
|
1063
1123
|
*
|
|
@@ -1074,15 +1134,14 @@ class PrettierIgnoreGenerated extends Component {
|
|
|
1074
1134
|
for (const sub of this.project.subprojects) {
|
|
1075
1135
|
if (!(sub instanceof DBXToolsTypeScriptProject)) continue;
|
|
1076
1136
|
const rel = toPosix(relative(rootAbs, sub.outdir));
|
|
1077
|
-
prettier.addIgnorePattern(`${rel}/index
|
|
1078
|
-
prettier.addIgnorePattern(`${rel}/src/**/index.ts`);
|
|
1137
|
+
prettier.addIgnorePattern(`${rel}/index*.ts`);
|
|
1079
1138
|
for (const module of codegenModulePaths(sub.dbxToolsConfig.codegenInputs)) {
|
|
1080
1139
|
prettier.addIgnorePattern(`${rel}/${module}`);
|
|
1081
1140
|
}
|
|
1082
1141
|
}
|
|
1083
1142
|
const extraMembers = (this.project as DBXToolsNodeProject).extraWorkspaceMembers ?? [];
|
|
1084
1143
|
for (const member of extraMembers) {
|
|
1085
|
-
prettier.addIgnorePattern(`${member}/index
|
|
1144
|
+
prettier.addIgnorePattern(`${member}/index*.ts`);
|
|
1086
1145
|
}
|
|
1087
1146
|
}
|
|
1088
1147
|
}
|
|
@@ -1094,7 +1153,7 @@ const DEFAULT_OMIT_RELATIVE_PREFIX = ["node"];
|
|
|
1094
1153
|
function resolveOmitRelativePrefix(option: OneOrMany<string> | undefined): string[] {
|
|
1095
1154
|
const raw = option === undefined ? DEFAULT_OMIT_RELATIVE_PREFIX : option;
|
|
1096
1155
|
const list = Array.isArray(raw) ? raw : [raw];
|
|
1097
|
-
return list.map((segment) =>
|
|
1156
|
+
return list.map((segment) => toSlug(segment)).filter(Boolean);
|
|
1098
1157
|
}
|
|
1099
1158
|
|
|
1100
1159
|
/**
|
|
@@ -1104,7 +1163,7 @@ function resolveOmitRelativePrefix(option: OneOrMany<string> | undefined): strin
|
|
|
1104
1163
|
*/
|
|
1105
1164
|
function packageNameFor(scope: string, relPath: string, omitPrefixes: string[]): string {
|
|
1106
1165
|
const segments = relPath.split("/").filter(Boolean);
|
|
1107
|
-
if (segments.length > 1 && omitPrefixes.includes(
|
|
1166
|
+
if (segments.length > 1 && omitPrefixes.includes(toSlug(segments[0]!))) {
|
|
1108
1167
|
segments.shift();
|
|
1109
1168
|
}
|
|
1110
1169
|
return PackageIdentifier.of(scope, segments.join("/")).packageName;
|
|
@@ -1145,14 +1204,14 @@ function engineSelfDependency(project: javascript.NodeProject): string | undefin
|
|
|
1145
1204
|
const enginePkgJson = join(resolvePkgRoot(), "package.json");
|
|
1146
1205
|
if (!toPosix(enginePkgJson).includes("/node_modules/")) return undefined;
|
|
1147
1206
|
const engine = readPackageManifest(dirname(enginePkgJson));
|
|
1148
|
-
const name =
|
|
1207
|
+
const name = trimToNull(engine?.name);
|
|
1149
1208
|
if (!name) return undefined;
|
|
1150
|
-
const version =
|
|
1209
|
+
const version = trimToNull(engine?.version);
|
|
1151
1210
|
|
|
1152
1211
|
// No existing consumer manifest (or no entry) falls through to a computed pin.
|
|
1153
1212
|
const consumer = readPackageManifest(resolve(project.outdir));
|
|
1154
1213
|
const dependencyOf = (field: unknown): string | undefined =>
|
|
1155
|
-
|
|
1214
|
+
isRecord(field) ? (trimToNull(field[name]) ?? undefined) : undefined;
|
|
1156
1215
|
const existing = dependencyOf(consumer?.devDependencies) ?? dependencyOf(consumer?.dependencies);
|
|
1157
1216
|
if (existing) return `${name}@${existing}`;
|
|
1158
1217
|
return `${name}@^${version}`;
|
|
@@ -1174,7 +1233,7 @@ function tagPathMatches(key: string, p: DiscoveredPackage): boolean {
|
|
|
1174
1233
|
return true;
|
|
1175
1234
|
}
|
|
1176
1235
|
// Otherwise treat the key as a glob against the same targets.
|
|
1177
|
-
const isMatch =
|
|
1236
|
+
const isMatch = toPathMatcher(key);
|
|
1178
1237
|
return isMatch(p.relPath) || isMatch(p.memberPath) || p.tagCandidates.some((c) => isMatch(c));
|
|
1179
1238
|
}
|
|
1180
1239
|
|
|
@@ -1278,6 +1337,12 @@ function initProject(
|
|
|
1278
1337
|
const roots =
|
|
1279
1338
|
options.packageRoots ??
|
|
1280
1339
|
(existsSync(resolve(project.outdir, "packages/js")) ? ["packages/js"] : DEFAULT_PACKAGE_ROOTS);
|
|
1340
|
+
const rootAbs = resolve(project.outdir);
|
|
1341
|
+
const configuredAppRoot =
|
|
1342
|
+
options.appRoot === false
|
|
1343
|
+
? undefined
|
|
1344
|
+
: (options.appRoot ??
|
|
1345
|
+
(existsSync(resolve(rootAbs, "packages/app")) ? "packages/app" : undefined));
|
|
1281
1346
|
project.dbxToolsConfig.packageRoots = [...roots];
|
|
1282
1347
|
if (options.syncResynthPaths?.length) {
|
|
1283
1348
|
project.dbxToolsConfig.syncResynthPaths = [...options.syncResynthPaths];
|
|
@@ -1317,11 +1382,11 @@ function initProject(
|
|
|
1317
1382
|
".npmrc",
|
|
1318
1383
|
);
|
|
1319
1384
|
for (const root of roots) {
|
|
1320
|
-
project.annotateGenerated(`/${root}/**/index
|
|
1385
|
+
project.annotateGenerated(`/${root}/**/index*.ts`);
|
|
1321
1386
|
project.annotateGenerated(`/${root}/**/src/generated/**`);
|
|
1322
1387
|
}
|
|
1323
1388
|
for (const member of project.extraWorkspaceMembers) {
|
|
1324
|
-
project.annotateGenerated(`/${member}/index
|
|
1389
|
+
project.annotateGenerated(`/${member}/index*.ts`);
|
|
1325
1390
|
}
|
|
1326
1391
|
|
|
1327
1392
|
// ESLint lives ONLY on the root and lints every package. `projectService` resolves
|
|
@@ -1350,11 +1415,15 @@ function initProject(
|
|
|
1350
1415
|
// Generated read-only outputs (barrels, app scripts, and codegen). ESLint
|
|
1351
1416
|
// --fix cannot rewrite files owned by the barrel generator, codegen, or projen.
|
|
1352
1417
|
for (const root of roots) {
|
|
1353
|
-
eslint.addIgnorePattern(`${root}/**/index
|
|
1418
|
+
eslint.addIgnorePattern(`${root}/**/index*.ts`);
|
|
1354
1419
|
}
|
|
1355
1420
|
eslint.addIgnorePattern("**/src/generated/**");
|
|
1356
1421
|
for (const member of project.extraWorkspaceMembers) {
|
|
1357
|
-
eslint.addIgnorePattern(`${member}/index
|
|
1422
|
+
eslint.addIgnorePattern(`${member}/index*.ts`);
|
|
1423
|
+
}
|
|
1424
|
+
if (configuredAppRoot) {
|
|
1425
|
+
eslint.addIgnorePattern(`${configuredAppRoot}/index*.ts`);
|
|
1426
|
+
eslint.addIgnorePattern(`${configuredAppRoot}/client/vite.config.ts`);
|
|
1358
1427
|
}
|
|
1359
1428
|
// The generated bun app scripts + unmanaged overrides live at the package root,
|
|
1360
1429
|
// outside any `src/**` tsconfig include, so the type-aware parser cannot resolve
|
|
@@ -1367,6 +1436,7 @@ function initProject(
|
|
|
1367
1436
|
// Codegen inputs may be added by mixins after construction; ignore their
|
|
1368
1437
|
// generated modules once typed package configuration is final.
|
|
1369
1438
|
new EslintIgnoreCodegen(project);
|
|
1439
|
+
new EslintWorkspaceImports(project);
|
|
1370
1440
|
eslint.addRules({
|
|
1371
1441
|
"import/no-relative-packages": "error",
|
|
1372
1442
|
// Monorepo tooling legitimately uses devDeps (typescript, tsx, projen) in src.
|
|
@@ -1396,6 +1466,9 @@ function initProject(
|
|
|
1396
1466
|
"tsconfig.json",
|
|
1397
1467
|
...roots.map((r) => `${r}/**/tsconfig.json`),
|
|
1398
1468
|
...project.extraWorkspaceMembers.map((member) => `${member}/tsconfig.json`),
|
|
1469
|
+
...(configuredAppRoot
|
|
1470
|
+
? [`${configuredAppRoot}/tsconfig.client.json`, `${configuredAppRoot}/tsconfig.server.json`]
|
|
1471
|
+
: []),
|
|
1399
1472
|
];
|
|
1400
1473
|
}
|
|
1401
1474
|
|
|
@@ -1410,7 +1483,6 @@ function initProject(
|
|
|
1410
1483
|
};
|
|
1411
1484
|
|
|
1412
1485
|
// Already-attached subprojects, keyed by repo-relative member path.
|
|
1413
|
-
const rootAbs = resolve(project.outdir);
|
|
1414
1486
|
const existing = new Map<string, DBXToolsJavaScriptProject>();
|
|
1415
1487
|
for (const sub of project.subprojects) {
|
|
1416
1488
|
if (sub instanceof DBXToolsNodeProject || sub instanceof DBXToolsTypeScriptProject) {
|
|
@@ -1418,12 +1490,52 @@ function initProject(
|
|
|
1418
1490
|
}
|
|
1419
1491
|
}
|
|
1420
1492
|
|
|
1493
|
+
let application: DBXToolsTypeScriptProject | undefined;
|
|
1494
|
+
let applicationPath: string | undefined;
|
|
1495
|
+
if (configuredAppRoot) {
|
|
1496
|
+
const appPath = toPosix(relative(rootAbs, resolve(rootAbs, configuredAppRoot)));
|
|
1497
|
+
if (!appPath || appPath.startsWith("../")) {
|
|
1498
|
+
throw new Error(`appRoot must resolve beneath the workspace root: ${configuredAppRoot}`);
|
|
1499
|
+
}
|
|
1500
|
+
applicationPath = appPath;
|
|
1501
|
+
const segments = appPath.split("/").filter(Boolean);
|
|
1502
|
+
const discovered = new DiscoveredPackage(rootAbs, segments[0]!, segments.slice(1));
|
|
1503
|
+
const found = existing.get(appPath);
|
|
1504
|
+
if (found) {
|
|
1505
|
+
if (!(found instanceof DBXToolsTypeScriptProject)) {
|
|
1506
|
+
throw new Error(`appRoot must be a TypeScript project: ${appPath}`);
|
|
1507
|
+
}
|
|
1508
|
+
application = found;
|
|
1509
|
+
} else {
|
|
1510
|
+
const defaults = {
|
|
1511
|
+
name: packageNameFor(project.scope, discovered.relPath, omitPrefixes),
|
|
1512
|
+
...options.packageDefaults,
|
|
1513
|
+
publishable: false,
|
|
1514
|
+
} satisfies DiscoveredTypeScriptPackageOptions;
|
|
1515
|
+
const packageOptions = options.resolvePackageOptions?.(discovered, defaults) ?? defaults;
|
|
1516
|
+
application = new DBXToolsTypeScriptProject({
|
|
1517
|
+
parent: project,
|
|
1518
|
+
outdir: appPath,
|
|
1519
|
+
...packageOptions,
|
|
1520
|
+
publishable: false,
|
|
1521
|
+
tags: [],
|
|
1522
|
+
});
|
|
1523
|
+
existing.set(appPath, application);
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1421
1527
|
// Discover + append a child per src-bearing folder. A root encapsulating an
|
|
1422
1528
|
// already-attached project doesn't re-create it, it just unions the tags in. The
|
|
1423
1529
|
// agnostic floor is set in the child's constructor; per-tag deps/tsconfig come from
|
|
1424
1530
|
// the PACKAGE_TAG_MIXINS applied across the subtree below.
|
|
1425
1531
|
for (const p of scanPackages(rootAbs, roots)) {
|
|
1426
|
-
|
|
1532
|
+
if (
|
|
1533
|
+
applicationPath &&
|
|
1534
|
+
(p.memberPath === applicationPath || p.memberPath.startsWith(`${applicationPath}/`))
|
|
1535
|
+
) {
|
|
1536
|
+
continue;
|
|
1537
|
+
}
|
|
1538
|
+
const tags = [...sequence(p.tagCandidates, resolveTags(p, tagPaths)).distinct()];
|
|
1427
1539
|
const found = existing.get(p.memberPath);
|
|
1428
1540
|
if (found) {
|
|
1429
1541
|
found.dbxToolsConfig.tags.push(...tags);
|
|
@@ -1444,7 +1556,7 @@ function initProject(
|
|
|
1444
1556
|
}
|
|
1445
1557
|
|
|
1446
1558
|
// The root project may itself carry tags (via a `""`/`"."` tag-path key).
|
|
1447
|
-
const rootTags = [...
|
|
1559
|
+
const rootTags = [...sequence(tagPaths[""], tagPaths["."]).distinct()];
|
|
1448
1560
|
if (rootTags.length) project.dbxToolsConfig.tags.push(...rootTags);
|
|
1449
1561
|
|
|
1450
1562
|
// Apply per-tag mixins across the whole subtree now that every child exists
|
|
@@ -1455,19 +1567,13 @@ function initProject(
|
|
|
1455
1567
|
}
|
|
1456
1568
|
const appkitMode = options.appkit ?? "auto";
|
|
1457
1569
|
if (appkitMode !== false) {
|
|
1458
|
-
|
|
1459
|
-
(
|
|
1460
|
-
|
|
1461
|
-
)
|
|
1462
|
-
const apps = projects.filter((candidate) => candidate.dbxToolsConfig.tags.includes("app"));
|
|
1463
|
-
const servers = projects.filter((candidate) =>
|
|
1464
|
-
candidate.dbxToolsConfig.tags.includes("server"),
|
|
1465
|
-
);
|
|
1466
|
-
const enabled = appkitMode === true || (apps.length > 0 && servers.length > 0);
|
|
1467
|
-
if (enabled) {
|
|
1570
|
+
if (appkitMode === true && !application) {
|
|
1571
|
+
throw new Error("appkit: true requires an application root");
|
|
1572
|
+
}
|
|
1573
|
+
if (application && (appkitMode === true || isAppKitApplication(application))) {
|
|
1468
1574
|
new AppKitWorkspace(project, {
|
|
1469
|
-
|
|
1470
|
-
|
|
1575
|
+
application,
|
|
1576
|
+
bundle: options.appBundle,
|
|
1471
1577
|
validate: appkitMode === true,
|
|
1472
1578
|
});
|
|
1473
1579
|
}
|
package/src/project-predicate.ts
CHANGED
|
@@ -3,14 +3,8 @@
|
|
|
3
3
|
* package identity, tags, and repository path.
|
|
4
4
|
*/
|
|
5
5
|
import { relative } from "node:path";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
object,
|
|
9
|
-
predicate,
|
|
10
|
-
Sequence,
|
|
11
|
-
type OneOrMany,
|
|
12
|
-
type Predicate,
|
|
13
|
-
} from "@dbx-tools/shared-core";
|
|
6
|
+
import { PathMatchInput, PathMatchPredicate, toPathMatcher } from "@dbx-tools/path";
|
|
7
|
+
import { Sequence, type OneOrMany, type Predicate, sequence, create } from "@dbx-tools/shared-core";
|
|
14
8
|
import { IConstruct } from "constructs";
|
|
15
9
|
import { Project } from "projen";
|
|
16
10
|
import { toPosix } from "./packages.ts";
|
|
@@ -28,7 +22,7 @@ import type { DBXToolsProject } from "./project.ts";
|
|
|
28
22
|
* silently reject that construct before the install problem can be diagnosed.
|
|
29
23
|
*/
|
|
30
24
|
export function isProject(): Predicate<IConstruct, Project> {
|
|
31
|
-
return
|
|
25
|
+
return create((c: IConstruct): c is Project => Project.isProject(c));
|
|
32
26
|
}
|
|
33
27
|
|
|
34
28
|
/** Guard: the construct implements the language-agnostic {@link DBXToolsProject} contract. */
|
|
@@ -53,9 +47,8 @@ export function isDBXToolsJavaScriptProject(): Predicate<IConstruct, DBXToolsJav
|
|
|
53
47
|
* the resulting predicate.
|
|
54
48
|
*/
|
|
55
49
|
function projectMatchers(...inputs: OneOrMany<PathMatchInput>): Sequence<PathMatchPredicate> {
|
|
56
|
-
return
|
|
57
|
-
.
|
|
58
|
-
.map((input) => match.toPathMatcher(input))
|
|
50
|
+
return sequence(inputs)
|
|
51
|
+
.map((input) => toPathMatcher(input))
|
|
59
52
|
.cache();
|
|
60
53
|
}
|
|
61
54
|
|
package/src/project-py.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/** Reusable uv workspace generation for Python packages hosted in a projen tree. */
|
|
2
2
|
import { existsSync, readdirSync } from "node:fs";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
4
|
+
import { projectUtils } from "@dbx-tools/core";
|
|
5
|
+
import { type OneOrMany, toSlug, dedent } from "@dbx-tools/shared-core";
|
|
6
6
|
import { Component, License, TextFile, type Project, github, javascript, python } from "projen";
|
|
7
7
|
import { JobPermission, type Job, type JobStep } from "projen/lib/github/workflows-model";
|
|
8
8
|
import { header, PYTHON_GENERATED_PACKAGE } from "./generated.ts";
|
|
@@ -350,8 +350,8 @@ export class DBXToolsPythonWorkspace extends Component {
|
|
|
350
350
|
constructor(project: javascript.NodeProject, options: DBXToolsPythonWorkspaceOptions) {
|
|
351
351
|
super(project);
|
|
352
352
|
const scope = isDBXToolsJavaScriptProject()(project)
|
|
353
|
-
?
|
|
354
|
-
:
|
|
353
|
+
? toSlug(project.scope)
|
|
354
|
+
: toSlug(project.name).replace(/-root$/, "");
|
|
355
355
|
const repositoryUrl =
|
|
356
356
|
options.repository?.url ??
|
|
357
357
|
projectRepositoryUrl(project) ??
|
|
@@ -379,7 +379,7 @@ export class DBXToolsPythonWorkspace extends Component {
|
|
|
379
379
|
directory,
|
|
380
380
|
description: `${scope} ${directory} Python package.`,
|
|
381
381
|
...pkg,
|
|
382
|
-
name: pkg?.name ?? `${scope}-${
|
|
382
|
+
name: pkg?.name ?? `${scope}-${toSlug(directory)}`,
|
|
383
383
|
module: pkg?.module ?? pythonModuleName(scope, directory),
|
|
384
384
|
};
|
|
385
385
|
});
|
|
@@ -802,7 +802,7 @@ export class DBXToolsPythonWorkspace extends Component {
|
|
|
802
802
|
marker: true,
|
|
803
803
|
readonly: true,
|
|
804
804
|
// prettier-ignore
|
|
805
|
-
lines:
|
|
805
|
+
lines: dedent(
|
|
806
806
|
// ============================================================================
|
|
807
807
|
/*js*/`
|
|
808
808
|
#!/usr/bin/env bun
|