@dbx-tools/projen 0.9.153 → 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.
Files changed (50) hide show
  1. package/README.md +44 -52
  2. package/index.ts +43 -21
  3. package/package.json +8 -8
  4. package/src/_task-command.ts +7 -7
  5. package/src/appkit-workspace.ts +278 -104
  6. package/src/barrels.ts +147 -259
  7. package/src/bun-app.ts +54 -10
  8. package/src/clean.ts +6 -6
  9. package/src/engine-root.ts +4 -4
  10. package/src/mixin.ts +0 -2
  11. package/src/module-exports.ts +4 -5
  12. package/src/npm-packaging.ts +128 -0
  13. package/src/npm-release.ts +200 -0
  14. package/src/packages.ts +11 -11
  15. package/src/project-js.ts +209 -103
  16. package/src/project-predicate.ts +5 -12
  17. package/src/project-py.ts +8 -6
  18. package/src/project.ts +7 -14
  19. package/src/publish.ts +3 -3
  20. package/src/python-node-bindings.ts +16 -12
  21. package/src/python-packaging.ts +177 -0
  22. package/src/release-context.ts +4 -0
  23. package/src/release-options.ts +2 -2
  24. package/src/release.ts +133 -13
  25. package/src/shared-core-usage.ts +17 -4
  26. package/src/tags.ts +10 -9
  27. package/src/watch.ts +7 -9
  28. package/src/workspace-lock.ts +2 -2
  29. package/src/workspace-manifest.ts +2 -2
  30. package/tasks/appkit-deploy.ts +35 -15
  31. package/tasks/barrels.ts +6 -9
  32. package/tasks/bump.ts +1 -1
  33. package/tasks/clean.ts +3 -3
  34. package/tasks/cli.ts +3 -3
  35. package/tasks/compile-workspace.ts +8 -8
  36. package/tasks/dev-watch.ts +11 -11
  37. package/tasks/local-publish.ts +6 -6
  38. package/tasks/publish-npm.ts +11 -317
  39. package/tasks/publish-python.ts +2 -180
  40. package/tasks/publish.ts +6 -11
  41. package/tasks/python-node-bindings.ts +2 -2
  42. package/tasks/python-registry.ts +6 -6
  43. package/tasks/python-sync.ts +2 -3
  44. package/tasks/release-version.ts +1 -1
  45. package/tasks/release.ts +11 -11
  46. package/tasks/sync.ts +2 -2
  47. package/tasks/test-workspace.ts +9 -9
  48. package/tasks/verify-publications.ts +273 -0
  49. package/tasks/version-check.ts +5 -5
  50. /package/{tasks → src}/python-release.ts +0 -0
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env -S bun
2
2
  /** Validate one stable release version against the canonical workspace policy. */
3
3
  import { resolve } from "node:path";
4
- import * as projectUtils from "@dbx-tools/core/project-utils";
4
+ import { projectUtils } from "@dbx-tools/core";
5
5
  import { z } from "zod";
6
6
  import { runTaskMain, taskCommand, taskOptions } from "./cli.ts";
7
7
  import { TaskRootOptionSchema } from "./options.ts";
package/tasks/release.ts CHANGED
@@ -2,8 +2,8 @@
2
2
  /** Run the configured release transaction. */
3
3
  import { readFileSync } from "node:fs";
4
4
  import { join } from "node:path";
5
- import * as projectUtils from "@dbx-tools/core/project-utils";
6
- import { errorUtils, json, log } from "@dbx-tools/shared-core";
5
+ import { projectUtils, type ProjectGhAccount, resolveProjectGhAccount } from "@dbx-tools/core";
6
+ import { log, toError, parse } from "@dbx-tools/shared-core";
7
7
  import type { Command } from "commander";
8
8
  import { z } from "zod";
9
9
  import { parsedTaskOptions, runTaskMain, taskCommand } from "./cli.ts";
@@ -67,7 +67,7 @@ const GithubReleaseRunViewSchema = z
67
67
 
68
68
  /** Inputs required to locate and watch one tag-triggered release workflow. */
69
69
  export interface WatchReleaseGithubActionsOptions {
70
- readonly account: projectUtils.ProjectGhAccount;
70
+ readonly account: ProjectGhAccount;
71
71
  readonly head: string;
72
72
  readonly root: string;
73
73
  readonly tag: string;
@@ -80,7 +80,7 @@ export type ReleaseGithubActionsWatcher = (
80
80
  options: WatchReleaseGithubActionsOptions,
81
81
  ) => Promise<void>;
82
82
 
83
- function githubRepository(account: projectUtils.ProjectGhAccount): string {
83
+ function githubRepository(account: ProjectGhAccount): string {
84
84
  return account.host === "github.com"
85
85
  ? account.remote.repository
86
86
  : `${account.host}/${account.remote.repository}`;
@@ -194,7 +194,7 @@ export async function watchReleaseGithubActions(
194
194
  ],
195
195
  { check: true, env: account.env, stderr: "inherit" },
196
196
  );
197
- const parsed = z.array(GithubReleaseRunSchema).safeParse(json.parse(output, []));
197
+ const parsed = z.array(GithubReleaseRunSchema).safeParse(parse(output, []));
198
198
  if (!parsed.success) throw new Error("GitHub returned invalid release workflow metadata");
199
199
  run = parsed.data.find(
200
200
  (candidate) => candidate.headSha === head && candidate.headBranch === tag,
@@ -234,7 +234,7 @@ export async function watchReleaseGithubActions(
234
234
  ],
235
235
  { check: true, env: account.env, stderr: "inherit" },
236
236
  );
237
- const parsed = GithubReleaseRunViewSchema.safeParse(json.parse(output));
237
+ const parsed = GithubReleaseRunViewSchema.safeParse(parse(output));
238
238
  if (!parsed.success) throw new Error("GitHub returned invalid release progress metadata");
239
239
  reportGithubProgress(parsed.data, progress);
240
240
  completed = parsed.data.status === "completed";
@@ -251,7 +251,7 @@ export async function watchReleaseGithubActions(
251
251
  }
252
252
  } catch (error) {
253
253
  logger.warn("GitHub Actions release watch stopped; the remote run was not cancelled", {
254
- error: errorUtils.toError(error),
254
+ error: toError(error),
255
255
  runId: run.databaseId,
256
256
  url: run.url,
257
257
  });
@@ -305,7 +305,7 @@ function pushAnnotatedReleaseTag(options: {
305
305
  }): void {
306
306
  const { annotation, branch, head, remote, root, tag } = options;
307
307
  runTaskCommand(root, "git", ["tag", "--annotate", tag, "--message", annotation]);
308
- const githubAccount = projectUtils.resolveProjectGhAccount(root, { remote });
308
+ const githubAccount = resolveProjectGhAccount(root, { remote });
309
309
  if (!githubAccount) {
310
310
  logger.warn(
311
311
  "no authenticated GitHub account can access the release repository; using git push",
@@ -382,7 +382,7 @@ function pushAnnotatedReleaseTag(options: {
382
382
  } catch (error) {
383
383
  if (remoteRefCreated) throw error;
384
384
  logger.warn("GitHub API tag creation failed; using git push", {
385
- error: errorUtils.toError(error),
385
+ error: toError(error),
386
386
  repository: githubAccount.remote.repository,
387
387
  });
388
388
  runTaskCommand(root, "git", ["push", remote, `refs/tags/${tag}`]);
@@ -480,7 +480,7 @@ export async function runRelease(
480
480
  /** After local release actions, wait for and stream the matching GitHub Actions run. */
481
481
  readonly watchGithubActions?: boolean;
482
482
  readonly githubActionsWatcher?: ReleaseGithubActionsWatcher;
483
- readonly resolveGithubAccount?: typeof projectUtils.resolveProjectGhAccount;
483
+ readonly resolveGithubAccount?: typeof resolveProjectGhAccount;
484
484
  },
485
485
  ): Promise<string> {
486
486
  const { branch, prefix, remote, root } = options;
@@ -606,7 +606,7 @@ export async function runRelease(
606
606
  }
607
607
 
608
608
  if (shouldWatchReleaseGithubActions(options, selection)) {
609
- const account = (options.resolveGithubAccount ?? projectUtils.resolveProjectGhAccount)(root, {
609
+ const account = (options.resolveGithubAccount ?? resolveProjectGhAccount)(root, {
610
610
  remote,
611
611
  });
612
612
  if (account) {
package/tasks/sync.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env -S bun
2
2
  /** Synthesize once or supervise Projen, barrels, and registered focused watchers. */
3
3
  import { fileURLToPath } from "node:url";
4
- import { errorUtils, log } from "@dbx-tools/shared-core";
4
+ import { log, toError } from "@dbx-tools/shared-core";
5
5
  import concurrently from "concurrently";
6
6
  import { z } from "zod";
7
7
  import { runTaskMain, taskCommand, taskOptions } from "./cli.ts";
@@ -84,7 +84,7 @@ export async function main(args: string[] = process.argv.slice(2)): Promise<void
84
84
  } catch (err) {
85
85
  // A tree that doesn't synth is precisely when the watcher is most useful: the edit
86
86
  // that repairs it is the one the projenrc watcher is sitting there waiting for.
87
- logger.error("initial sync failed - watching anyway", { error: errorUtils.toError(err) });
87
+ logger.error("initial sync failed - watching anyway", { error: toError(err) });
88
88
  }
89
89
 
90
90
  const watchers = [
@@ -1,8 +1,8 @@
1
1
  import { readFileSync, writeFileSync } from "node:fs";
2
2
  import { basename, join, relative, resolve, sep } from "node:path";
3
- import * as exec from "@dbx-tools/core/exec";
4
- import { findFilesSync } from "@dbx-tools/path/find";
5
- import { log, object } from "@dbx-tools/shared-core";
3
+ import { spawnSync, spawn } from "@dbx-tools/core";
4
+ import { findFilesSync } from "@dbx-tools/path";
5
+ import { log, sequence } from "@dbx-tools/shared-core";
6
6
  import { z } from "zod";
7
7
  import { runTaskMain, taskCommand, taskOptions, taskPositionals, taskRoot } from "./cli.ts";
8
8
  import { TaskRootOptionSchema } from "./options.ts";
@@ -137,7 +137,7 @@ function changedFiles(root: string, base?: string): string[] {
137
137
  const candidates = base ? [base] : ["origin/main", "main", "HEAD~1"];
138
138
  let committed: string[] = [];
139
139
  for (const candidate of candidates) {
140
- const result = exec.spawnSync("git", ["diff", "--name-only", `${candidate}...HEAD`], {
140
+ const result = spawnSync("git", ["diff", "--name-only", `${candidate}...HEAD`], {
141
141
  cwd: root,
142
142
  stdin: "ignore",
143
143
  stdout: "capture",
@@ -148,14 +148,14 @@ function changedFiles(root: string, base?: string): string[] {
148
148
  break;
149
149
  }
150
150
  }
151
- const working = exec.spawnSync("git", ["diff", "--name-only", "HEAD"], {
151
+ const working = spawnSync("git", ["diff", "--name-only", "HEAD"], {
152
152
  cwd: root,
153
153
  stdin: "ignore",
154
154
  stdout: "capture",
155
155
  stderr: "ignore",
156
156
  check: true,
157
157
  }).stdoutLines;
158
- return [...object.sequence(committed, working).filter(Boolean).distinct()].sort();
158
+ return [...sequence(committed, working).filter(Boolean).distinct()].sort();
159
159
  }
160
160
 
161
161
  function selectedTests(
@@ -203,13 +203,13 @@ function runnerGroup(root: string, path: string): string {
203
203
  if (relativePath.startsWith("packages/js/shared/")) return "browser-shared";
204
204
  if (
205
205
  relativePath.startsWith("packages/js/ui/") ||
206
- relativePath.startsWith("packages/example/app/")
206
+ relativePath.startsWith("packages/demo/client/")
207
207
  ) {
208
208
  return "react-ui";
209
209
  }
210
210
  if (relativePath.startsWith("projen/")) return "projen";
211
211
  if (relativePath.startsWith("docs/scripts/")) return "docs";
212
- if (relativePath.startsWith("packages/example/")) return "examples";
212
+ if (relativePath.startsWith("packages/demo/")) return "demo";
213
213
  return "node";
214
214
  }
215
215
 
@@ -228,7 +228,7 @@ async function runTests(root: string, files: readonly string[]): Promise<void> {
228
228
  const started = performance.now();
229
229
  const jobs = [...groups].map(async ([name, paths]) => {
230
230
  const groupStarted = performance.now();
231
- const result = await exec.spawn("bun", ["test", ...paths], {
231
+ const result = await spawn("bun", ["test", ...paths], {
232
232
  cwd: root,
233
233
  stdin: "ignore",
234
234
  stdout: "inherit",
@@ -0,0 +1,273 @@
1
+ #!/usr/bin/env -S bun
2
+ /** Wait for one release batch to become available from public package registries. */
3
+ import { createHash } from "node:crypto";
4
+ import { readFileSync, readdirSync } from "node:fs";
5
+ import { basename, join, resolve } from "node:path";
6
+ import { log, poll, mapConcurrent, toError } from "@dbx-tools/shared-core";
7
+ import { z } from "zod";
8
+ import { runTaskMain, taskCommand, taskOptions } from "./cli.ts";
9
+ import {
10
+ npmReleaseMatches,
11
+ publishedNpmRelease,
12
+ readNpmArchiveIdentity,
13
+ } from "../src/npm-release.ts";
14
+ import { PUBLIC_NPM_REGISTRY_URL, PUBLIC_PYPI_REGISTRY_URL } from "../src/release-context.ts";
15
+
16
+ const DEFAULT_CONCURRENCY = 8;
17
+ const DEFAULT_INTERVAL_SECONDS = 5;
18
+ const DEFAULT_TIMEOUT_SECONDS = 10 * 60;
19
+ const logger = log.logger("projen:verify-publications");
20
+
21
+ const PypiReleaseSchema = z
22
+ .object({
23
+ urls: z
24
+ .array(
25
+ z
26
+ .object({
27
+ filename: z.string().describe("Published distribution filename"),
28
+ digests: z
29
+ .object({
30
+ sha256: z.string().describe("Published distribution SHA-256 digest"),
31
+ })
32
+ .describe("Published distribution digests"),
33
+ })
34
+ .describe("One published Python distribution"),
35
+ )
36
+ .describe("Published Python distributions"),
37
+ })
38
+ .describe("PyPI release metadata");
39
+
40
+ interface PypiReleaseIdentity {
41
+ readonly files: ReadonlyMap<string, string>;
42
+ readonly name: string;
43
+ readonly version: string;
44
+ }
45
+
46
+ /** One public artifact group checked during the shared availability barrier. */
47
+ export interface PublicationProbe {
48
+ /** Human-readable package identity used in progress and timeout messages. */
49
+ readonly label: string;
50
+ /** Return true only when the exact expected public artifact is available. */
51
+ available(signal: AbortSignal): boolean | PromiseLike<boolean>;
52
+ }
53
+
54
+ /** Timing and concurrency controls for the shared publication barrier. */
55
+ export interface PublicationWaitOptions {
56
+ readonly concurrency?: number;
57
+ readonly intervalMs?: number;
58
+ readonly timeoutMs?: number;
59
+ }
60
+
61
+ /** Poll every unresolved publication concurrently and stop after one shared timeout. */
62
+ export async function waitForPublications(
63
+ probes: readonly PublicationProbe[],
64
+ options: PublicationWaitOptions = {},
65
+ ): Promise<void> {
66
+ if (probes.length === 0) throw new Error("No public publications were provided");
67
+ const concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;
68
+ const errors = new Map<string, Error>();
69
+ let pending = [...probes];
70
+ let previousCount: number | undefined;
71
+ try {
72
+ for await (const unavailable of poll<PublicationProbe[]>(
73
+ async ({ signal }) => {
74
+ const results = await mapConcurrent(
75
+ pending,
76
+ async (probe) => {
77
+ try {
78
+ const available = await probe.available(signal);
79
+ if (available) errors.delete(probe.label);
80
+ return { available, probe };
81
+ } catch (error) {
82
+ errors.set(probe.label, toError(error));
83
+ return { available: false, probe };
84
+ }
85
+ },
86
+ { concurrency },
87
+ );
88
+ return results.filter(({ available }) => !available).map(({ probe }) => probe);
89
+ },
90
+ {
91
+ intervalMs: options.intervalMs ?? DEFAULT_INTERVAL_SECONDS * 1_000,
92
+ timeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_SECONDS * 1_000,
93
+ predicate: (unavailable) => unavailable.length > 0,
94
+ },
95
+ )) {
96
+ pending = unavailable;
97
+ if (pending.length > 0 && pending.length !== previousCount) {
98
+ logger.info(
99
+ `waiting for ${pending.length} public publication${pending.length === 1 ? "" : "s"}`,
100
+ );
101
+ }
102
+ previousCount = pending.length;
103
+ }
104
+ } catch (error) {
105
+ if (pending.length === 0) throw error;
106
+ const details = pending
107
+ .map((probe) => {
108
+ const failure = errors.get(probe.label);
109
+ return failure ? `${probe.label}: ${failure.message}` : probe.label;
110
+ })
111
+ .join("\n");
112
+ throw new Error(`Publications did not become available:\n${details}`, { cause: error });
113
+ }
114
+ logger.info(`verified ${probes.length} public publication${probes.length === 1 ? "" : "s"}`);
115
+ }
116
+
117
+ function npmPublicationProbes(directory: string, version: string): PublicationProbe[] {
118
+ return readdirSync(directory)
119
+ .filter((file) => file.endsWith(".tgz"))
120
+ .sort()
121
+ .map((file) => {
122
+ const local = readNpmArchiveIdentity(join(directory, file));
123
+ if (local.version !== version) {
124
+ throw new Error(`${local.name} archive carries ${local.version}, expected ${version}`);
125
+ }
126
+ return {
127
+ label: `${local.name}@${local.version}`,
128
+ available: async (signal: AbortSignal) =>
129
+ npmReleaseMatches(
130
+ local,
131
+ await publishedNpmRelease(local.name, local.version, PUBLIC_NPM_REGISTRY_URL, signal),
132
+ ),
133
+ };
134
+ });
135
+ }
136
+
137
+ function distributionFiles(directory: string): string[] {
138
+ return readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
139
+ const path = join(directory, entry.name);
140
+ if (entry.isDirectory()) return distributionFiles(path);
141
+ return entry.name.endsWith(".whl") || entry.name.endsWith(".tar.gz") ? [path] : [];
142
+ });
143
+ }
144
+
145
+ function pythonDistributionName(file: string, version: string): string {
146
+ const name = basename(file);
147
+ const sourceSuffix = `-${version}.tar.gz`;
148
+ if (name.endsWith(sourceSuffix)) {
149
+ return name.slice(0, -sourceSuffix.length).replaceAll("_", "-").toLowerCase();
150
+ }
151
+ if (name.endsWith(".whl")) {
152
+ const marker = `-${version}-`;
153
+ const markerIndex = name.lastIndexOf(marker);
154
+ if (markerIndex > 0) {
155
+ return name.slice(0, markerIndex).replaceAll("_", "-").toLowerCase();
156
+ }
157
+ }
158
+ throw new Error(`Python distribution ${name} does not carry release version ${version}`);
159
+ }
160
+
161
+ function pypiReleaseIdentities(directory: string, version: string): PypiReleaseIdentity[] {
162
+ const releases = new Map<string, Map<string, string>>();
163
+ for (const path of distributionFiles(directory)) {
164
+ const name = pythonDistributionName(path, version);
165
+ const files = releases.get(name) ?? new Map<string, string>();
166
+ const filename = basename(path);
167
+ files.set(filename, createHash("sha256").update(readFileSync(path)).digest("hex"));
168
+ releases.set(name, files);
169
+ }
170
+ return [...releases]
171
+ .sort(([left], [right]) => left.localeCompare(right))
172
+ .map(([name, files]) => ({ files, name, version }));
173
+ }
174
+
175
+ function pypiPublicationProbes(directory: string, version: string): PublicationProbe[] {
176
+ return pypiReleaseIdentities(directory, version).map((local) => ({
177
+ label: `${local.name}==${local.version}`,
178
+ available: async (signal: AbortSignal) => {
179
+ const response = await fetch(
180
+ `${PUBLIC_PYPI_REGISTRY_URL}/${encodeURIComponent(local.name)}/${encodeURIComponent(local.version)}/json`,
181
+ {
182
+ headers: { accept: "application/json" },
183
+ signal,
184
+ },
185
+ );
186
+ if (response.status === 404) return false;
187
+ if (!response.ok) {
188
+ throw new Error(
189
+ `PyPI lookup failed for ${local.name}==${local.version}: ${response.status}`,
190
+ );
191
+ }
192
+ const published = PypiReleaseSchema.parse(await response.json());
193
+ const publishedFiles = new Map(
194
+ published.urls.map(({ filename, digests }) => [filename, digests.sha256]),
195
+ );
196
+ for (const [filename, digest] of local.files) {
197
+ const publishedDigest = publishedFiles.get(filename);
198
+ if (!publishedDigest) return false;
199
+ if (publishedDigest !== digest) {
200
+ throw new Error(
201
+ `PyPI content does not match ${local.name}==${local.version} ${filename}`,
202
+ );
203
+ }
204
+ }
205
+ return true;
206
+ },
207
+ }));
208
+ }
209
+
210
+ export const VerifyPublicationsOptionsSchema = z
211
+ .object({
212
+ version: z.string().trim().min(1).describe("Expected public release version"),
213
+ npmDirectory: z
214
+ .string()
215
+ .trim()
216
+ .min(1)
217
+ .optional()
218
+ .describe("Directory containing released npm archives"),
219
+ pypiDirectory: z
220
+ .string()
221
+ .trim()
222
+ .min(1)
223
+ .optional()
224
+ .describe("Directory containing released Python distributions"),
225
+ concurrency: z.coerce
226
+ .number()
227
+ .int()
228
+ .positive()
229
+ .default(DEFAULT_CONCURRENCY)
230
+ .describe("Maximum concurrent public registry checks"),
231
+ intervalSeconds: z.coerce
232
+ .number()
233
+ .positive()
234
+ .default(DEFAULT_INTERVAL_SECONDS)
235
+ .describe("Delay between shared publication checks"),
236
+ timeoutSeconds: z.coerce
237
+ .number()
238
+ .positive()
239
+ .default(DEFAULT_TIMEOUT_SECONDS)
240
+ .describe("Maximum time to wait for the complete publication batch"),
241
+ })
242
+ .refine((options) => options.npmDirectory || options.pypiDirectory, {
243
+ message: "At least one publication directory is required",
244
+ })
245
+ .describe("Options for the public package availability barrier");
246
+
247
+ /** Parse expected release artifacts and wait for their public registry records. */
248
+ export async function main(args: readonly string[] = process.argv.slice(2)): Promise<void> {
249
+ const options = await taskOptions(
250
+ taskCommand(
251
+ import.meta.url,
252
+ "Wait for released packages to become publicly available",
253
+ VerifyPublicationsOptionsSchema,
254
+ ),
255
+ VerifyPublicationsOptionsSchema,
256
+ args,
257
+ );
258
+ const probes = [
259
+ ...(options.npmDirectory
260
+ ? npmPublicationProbes(resolve(options.npmDirectory), options.version)
261
+ : []),
262
+ ...(options.pypiDirectory
263
+ ? pypiPublicationProbes(resolve(options.pypiDirectory), options.version)
264
+ : []),
265
+ ];
266
+ await waitForPublications(probes, {
267
+ concurrency: options.concurrency,
268
+ intervalMs: options.intervalSeconds * 1_000,
269
+ timeoutMs: options.timeoutSeconds * 1_000,
270
+ });
271
+ }
272
+
273
+ await runTaskMain(import.meta, main);
@@ -2,9 +2,9 @@
2
2
  /** Verify that every committed package version matches the root VERSION file. */
3
3
  import { readFileSync } from "node:fs";
4
4
  import { join } from "node:path";
5
- import * as projectUtils from "@dbx-tools/core/project-utils";
6
- import { find } from "@dbx-tools/path";
7
- import { json, log } from "@dbx-tools/shared-core";
5
+ import { projectUtils } from "@dbx-tools/core";
6
+ import { findFiles } from "@dbx-tools/path";
7
+ import { log, parseRecord } from "@dbx-tools/shared-core";
8
8
  import { parse } from "smol-toml";
9
9
  import { runTaskMain } from "./cli.ts";
10
10
  import { recordedPackages } from "../src/packages.ts";
@@ -13,7 +13,7 @@ import { readWorkspaceVersion } from "../src/workspace-version.ts";
13
13
  const logger = log.logger("projen:version-check");
14
14
 
15
15
  function manifestVersion(path: string): string | undefined {
16
- const manifest = json.parseRecord(readFileSync(path, "utf8"));
16
+ const manifest = parseRecord(readFileSync(path, "utf8"));
17
17
  return typeof manifest?.version === "string" ? manifest.version : undefined;
18
18
  }
19
19
 
@@ -37,7 +37,7 @@ export async function main(): Promise<void> {
37
37
  check(`${pkg.path}/package.json`, manifestVersion(join(pkg.dir, "package.json")));
38
38
  }
39
39
 
40
- for await (const path of find.findFiles("**/pyproject.toml", { cwd: root })) {
40
+ for await (const path of findFiles("**/pyproject.toml", { cwd: root })) {
41
41
  check(path, pythonVersion(join(root, path)));
42
42
  }
43
43
  if (mismatches.length > 0) {
File without changes