@dbx-tools/projen 0.9.138 → 0.9.140
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/index.ts +1 -1
- package/package.json +5 -5
- package/tasks/compile-workspace.ts +15 -22
- package/tasks/publish-npm.ts +43 -30
- package/tasks/test-workspace.ts +14 -15
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.140";
|
|
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";
|
package/package.json
CHANGED
|
@@ -30,10 +30,10 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@clack/prompts": "^1.7.0",
|
|
33
|
-
"@dbx-tools/cli-args": "^0.9.
|
|
34
|
-
"@dbx-tools/core": "^0.9.
|
|
35
|
-
"@dbx-tools/path": "^0.9.
|
|
36
|
-
"@dbx-tools/shared-core": "^0.9.
|
|
33
|
+
"@dbx-tools/cli-args": "^0.9.140",
|
|
34
|
+
"@dbx-tools/core": "^0.9.140",
|
|
35
|
+
"@dbx-tools/path": "^0.9.140",
|
|
36
|
+
"@dbx-tools/shared-core": "^0.9.140",
|
|
37
37
|
"commander": "^15.0.0",
|
|
38
38
|
"concurrently": "^10.0.3",
|
|
39
39
|
"constructs": "^10.6.0",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
},
|
|
56
56
|
"main": "index.ts",
|
|
57
57
|
"license": "Apache-2.0",
|
|
58
|
-
"version": "0.9.
|
|
58
|
+
"version": "0.9.140",
|
|
59
59
|
"types": "index.ts",
|
|
60
60
|
"type": "module",
|
|
61
61
|
"exports": {
|
|
@@ -5,6 +5,7 @@ import { availableParallelism } from "node:os";
|
|
|
5
5
|
import { join, resolve } from "node:path";
|
|
6
6
|
import * as exec from "@dbx-tools/core/exec";
|
|
7
7
|
import { errorUtils, log } from "@dbx-tools/shared-core";
|
|
8
|
+
import type { TaskSpec, TasksManifest } from "projen";
|
|
8
9
|
import { runTaskMain } from "./cli.ts";
|
|
9
10
|
|
|
10
11
|
const logger = log.logger("projen:compile");
|
|
@@ -15,20 +16,6 @@ interface PackageManifest {
|
|
|
15
16
|
readonly workspaces?: string[];
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
interface TaskManifest {
|
|
19
|
-
readonly tasks?: {
|
|
20
|
-
readonly "pre-compile"?: {
|
|
21
|
-
readonly steps?: Array<{ readonly exec?: string; readonly execArgs?: string[] }>;
|
|
22
|
-
};
|
|
23
|
-
readonly compile?: {
|
|
24
|
-
readonly steps?: Array<{ readonly exec?: string; readonly execArgs?: string[] }>;
|
|
25
|
-
};
|
|
26
|
-
readonly "post-compile"?: {
|
|
27
|
-
readonly steps?: Array<{ readonly exec?: string; readonly execArgs?: string[] }>;
|
|
28
|
-
};
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
|
|
32
19
|
export interface CompilePlan {
|
|
33
20
|
readonly typescriptConfigs: string[];
|
|
34
21
|
readonly customPackages: Array<{ readonly name: string; readonly directory: string }>;
|
|
@@ -38,9 +25,15 @@ function readJson<T>(path: string): T {
|
|
|
38
25
|
return JSON.parse(readFileSync(path, "utf8")) as T;
|
|
39
26
|
}
|
|
40
27
|
|
|
41
|
-
function
|
|
28
|
+
function hasTaskOptions(task: TaskSpec | undefined): boolean {
|
|
29
|
+
return Object.keys(task ?? {}).some((key) => !["name", "description", "steps"].includes(key));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function plainTypeScriptBuild(task: TaskSpec | undefined): boolean {
|
|
33
|
+
if (hasTaskOptions(task)) return false;
|
|
34
|
+
const steps = task?.steps;
|
|
42
35
|
if (!Array.isArray(steps) || steps.length !== 1) return false;
|
|
43
|
-
const step = steps[0]
|
|
36
|
+
const step = steps[0];
|
|
44
37
|
if (!step || typeof step !== "object") return false;
|
|
45
38
|
return (
|
|
46
39
|
Object.keys(step).length === 1 &&
|
|
@@ -50,8 +43,8 @@ function plainTypeScriptBuild(steps: unknown): boolean {
|
|
|
50
43
|
);
|
|
51
44
|
}
|
|
52
45
|
|
|
53
|
-
function
|
|
54
|
-
return
|
|
46
|
+
function hasLifecycle(task: TaskSpec | undefined): boolean {
|
|
47
|
+
return hasTaskOptions(task) || (task?.steps?.length ?? 0) > 0;
|
|
55
48
|
}
|
|
56
49
|
|
|
57
50
|
/** Read the root's concrete workspace paths on every run, including newly added members. */
|
|
@@ -65,13 +58,13 @@ export function compilePlan(root: string): CompilePlan {
|
|
|
65
58
|
const script = manifest.scripts?.compile;
|
|
66
59
|
if (!script) continue;
|
|
67
60
|
const taskPath = join(directory, ".projen", "tasks.json");
|
|
68
|
-
const tasks = existsSync(taskPath) ? readJson<
|
|
69
|
-
const plainCompile = plainTypeScriptBuild(tasks?.compile
|
|
61
|
+
const tasks = existsSync(taskPath) ? readJson<TasksManifest>(taskPath).tasks : undefined;
|
|
62
|
+
const plainCompile = plainTypeScriptBuild(tasks?.compile);
|
|
70
63
|
if (
|
|
71
64
|
script === "projen compile" &&
|
|
72
65
|
plainCompile &&
|
|
73
|
-
!
|
|
74
|
-
!
|
|
66
|
+
!hasLifecycle(tasks?.["pre-compile"]) &&
|
|
67
|
+
!hasLifecycle(tasks?.["post-compile"])
|
|
75
68
|
) {
|
|
76
69
|
typescriptConfigs.push(join(directory, "tsconfig.json"));
|
|
77
70
|
} else {
|
package/tasks/publish-npm.ts
CHANGED
|
@@ -15,13 +15,14 @@ import { tmpdir } from "node:os";
|
|
|
15
15
|
import { join, relative, resolve } from "node:path";
|
|
16
16
|
import * as exec from "@dbx-tools/core/exec";
|
|
17
17
|
import * as projectUtils from "@dbx-tools/core/project-utils";
|
|
18
|
-
import { log } from "@dbx-tools/shared-core";
|
|
18
|
+
import { asyncUtils, log } from "@dbx-tools/shared-core";
|
|
19
19
|
import { z } from "zod";
|
|
20
20
|
import { runTaskMain, taskCommand, taskOptions } from "./cli.ts";
|
|
21
|
-
import { TaskDryRunOptionSchema } from "./options.ts";
|
|
22
|
-
import {
|
|
21
|
+
import { TaskConcurrencyOptionSchema, TaskDryRunOptionSchema } from "./options.ts";
|
|
22
|
+
import { runTaskCommandAsync } from "../src/_task-command.ts";
|
|
23
23
|
|
|
24
24
|
const DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
|
25
|
+
const DEFAULT_PUBLISH_CONCURRENCY = 4;
|
|
25
26
|
const logger = log.logger("projen:publish-npm");
|
|
26
27
|
|
|
27
28
|
/** Registry and archive identity used to verify idempotent npm publication. */
|
|
@@ -258,7 +259,8 @@ export function readNpmArchiveIdentity(path: string): NpmReleaseIdentity {
|
|
|
258
259
|
};
|
|
259
260
|
}
|
|
260
261
|
|
|
261
|
-
|
|
262
|
+
/** Group npm archives into dependency-ordered layers that can publish concurrently. */
|
|
263
|
+
function npmArchiveLayers(archives: readonly string[]): string[][] {
|
|
262
264
|
const byName = new Map(
|
|
263
265
|
archives.map((archive) => {
|
|
264
266
|
const manifest = readNpmArchiveManifest(archive);
|
|
@@ -278,7 +280,7 @@ function orderNpmArchives(archives: readonly string[]): string[] {
|
|
|
278
280
|
),
|
|
279
281
|
]),
|
|
280
282
|
);
|
|
281
|
-
const
|
|
283
|
+
const layers: string[][] = [];
|
|
282
284
|
while (remaining.size > 0) {
|
|
283
285
|
const ready = [...remaining]
|
|
284
286
|
.filter(([, dependencies]) => dependencies.size === 0)
|
|
@@ -289,15 +291,15 @@ function orderNpmArchives(archives: readonly string[]): string[] {
|
|
|
289
291
|
`Cyclic npm release dependencies: ${[...remaining.keys()].sort().join(", ")}`,
|
|
290
292
|
);
|
|
291
293
|
}
|
|
294
|
+
layers.push(ready.map((name) => byName.get(name)!.archive));
|
|
292
295
|
for (const name of ready) {
|
|
293
|
-
ordered.push(byName.get(name)!.archive);
|
|
294
296
|
remaining.delete(name);
|
|
295
297
|
}
|
|
296
298
|
for (const dependencies of remaining.values()) {
|
|
297
299
|
for (const name of ready) dependencies.delete(name);
|
|
298
300
|
}
|
|
299
301
|
}
|
|
300
|
-
return
|
|
302
|
+
return layers;
|
|
301
303
|
}
|
|
302
304
|
|
|
303
305
|
/** Hash paths, executable bits, symlink targets, and bytes while ignoring tar metadata. */
|
|
@@ -414,41 +416,51 @@ export function packNpmPackage(
|
|
|
414
416
|
|
|
415
417
|
/** Publish dependency-ordered npm archives, skipping only exact registry matches. */
|
|
416
418
|
export async function publishNpmArchives(options: {
|
|
419
|
+
readonly concurrency?: number;
|
|
417
420
|
readonly directory: string;
|
|
418
421
|
readonly dryRun?: boolean;
|
|
419
422
|
readonly registry?: string;
|
|
420
423
|
readonly version?: string;
|
|
421
424
|
}): Promise<void> {
|
|
422
425
|
const directory = resolve(options.directory);
|
|
423
|
-
const
|
|
426
|
+
const layers = npmArchiveLayers(
|
|
424
427
|
readdirSync(directory)
|
|
425
428
|
.filter((file) => file.endsWith(".tgz"))
|
|
426
429
|
.sort()
|
|
427
430
|
.map((file) => join(directory, file)),
|
|
428
431
|
);
|
|
429
|
-
if (
|
|
432
|
+
if (layers.length === 0) throw new Error(`No npm archives found in ${directory}`);
|
|
430
433
|
|
|
431
|
-
for (const
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
434
|
+
for (const archives of layers) {
|
|
435
|
+
await asyncUtils.mapConcurrent(
|
|
436
|
+
archives,
|
|
437
|
+
async (archive) => {
|
|
438
|
+
const local = readNpmArchiveIdentity(archive);
|
|
439
|
+
if (options.version && local.version !== options.version) {
|
|
440
|
+
throw new Error(
|
|
441
|
+
`npm archive ${archive} carries ${local.version}, expected ${options.version}`,
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
if (!options.dryRun) {
|
|
445
|
+
const published = await publishedNpmRelease(local.name, local.version, options.registry);
|
|
446
|
+
if (npmReleaseMatches(local, published)) {
|
|
447
|
+
logger.info(`skip published ${local.name}@${local.version}`);
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
await runTaskCommandAsync(process.cwd(), "npm", [
|
|
452
|
+
"publish",
|
|
453
|
+
archive,
|
|
454
|
+
...(local.access ? ["--access", local.access] : []),
|
|
455
|
+
...(options.registry ? ["--registry", options.registry] : []),
|
|
456
|
+
...(options.dryRun ? ["--dry-run"] : []),
|
|
457
|
+
]);
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
concurrency: options.concurrency ?? DEFAULT_PUBLISH_CONCURRENCY,
|
|
461
|
+
errorMode: "settle",
|
|
462
|
+
},
|
|
463
|
+
);
|
|
452
464
|
}
|
|
453
465
|
}
|
|
454
466
|
|
|
@@ -464,6 +476,7 @@ export const PublishNpmOptionsSchema = z.object({
|
|
|
464
476
|
.describe("npm registry URL")
|
|
465
477
|
.meta({ env: "NPM_CONFIG_REGISTRY" }),
|
|
466
478
|
dryRun: TaskDryRunOptionSchema,
|
|
479
|
+
concurrency: TaskConcurrencyOptionSchema.default(DEFAULT_PUBLISH_CONCURRENCY),
|
|
467
480
|
});
|
|
468
481
|
|
|
469
482
|
/** Parse task options and publish validated npm release archives. */
|
package/tasks/test-workspace.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { basename, join, relative, resolve, sep } from "node:path";
|
|
3
3
|
import * as exec from "@dbx-tools/core/exec";
|
|
4
|
+
import { findFilesSync } from "@dbx-tools/path/find";
|
|
4
5
|
import { log, object } from "@dbx-tools/shared-core";
|
|
5
6
|
import { z } from "zod";
|
|
6
7
|
import { runTaskMain, taskCommand, taskOptions, taskPositionals, taskRoot } from "./cli.ts";
|
|
@@ -90,17 +91,14 @@ function writeGraph(root: string, graph: WorkspaceGraph): void {
|
|
|
90
91
|
}
|
|
91
92
|
|
|
92
93
|
function testFiles(directory: string): string[] {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
};
|
|
102
|
-
visit(directory);
|
|
103
|
-
return files.sort();
|
|
94
|
+
return [
|
|
95
|
+
...findFilesSync("**/*.{test,spec}.{js,jsx,ts,tsx,mjs,cjs,mts,cts}", {
|
|
96
|
+
cwd: directory,
|
|
97
|
+
absolute: true,
|
|
98
|
+
nodir: true,
|
|
99
|
+
ignoreOptions: { test: false },
|
|
100
|
+
}),
|
|
101
|
+
].sort();
|
|
104
102
|
}
|
|
105
103
|
|
|
106
104
|
function integrationTest(path: string): boolean {
|
|
@@ -117,13 +115,13 @@ function packageForPath(graph: WorkspaceGraph, path: string): WorkspaceGraphPack
|
|
|
117
115
|
.find((pkg) => normalized === pkg.path || normalized.startsWith(`${pkg.path}/`));
|
|
118
116
|
}
|
|
119
117
|
|
|
120
|
-
function affectedPackages(graph: WorkspaceGraph, changed: readonly string[]): Set<string> {
|
|
118
|
+
function affectedPackages(graph: WorkspaceGraph, changed: readonly string[]): Set<string> | "all" {
|
|
121
119
|
const byName = new Map(graph.packages.map((pkg) => [pkg.name, pkg]));
|
|
122
120
|
const affected = new Set<string>();
|
|
123
121
|
const pending: string[] = [];
|
|
124
122
|
for (const path of changed) {
|
|
125
123
|
const pkg = packageForPath(graph, path);
|
|
126
|
-
if (!pkg) return
|
|
124
|
+
if (!pkg) return "all";
|
|
127
125
|
if (!affected.has(pkg.name)) pending.push(pkg.name);
|
|
128
126
|
}
|
|
129
127
|
while (pending.length > 0) {
|
|
@@ -175,7 +173,7 @@ function selectedTests(
|
|
|
175
173
|
if (mode === "integration") return all.filter(integrationTest);
|
|
176
174
|
if (mode === "all") return all;
|
|
177
175
|
|
|
178
|
-
let names: Set<string
|
|
176
|
+
let names: Set<string> | "all";
|
|
179
177
|
if (mode === "changed") {
|
|
180
178
|
const changed = changedFiles(root, base);
|
|
181
179
|
if (changed.length === 0) return [];
|
|
@@ -190,6 +188,7 @@ function selectedTests(
|
|
|
190
188
|
names.add(byPath.name);
|
|
191
189
|
}
|
|
192
190
|
}
|
|
191
|
+
if (names === "all") return all;
|
|
193
192
|
const paths = new Set(graph.packages.filter((pkg) => names.has(pkg.name)).map((pkg) => pkg.path));
|
|
194
193
|
return all.filter((path) => {
|
|
195
194
|
const relativePath = toPosix(relative(root, path));
|