@dbx-tools/projen 0.9.173 → 0.9.175

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 CHANGED
@@ -669,6 +669,24 @@ whole-tree operation driven by the root `.projenrc.ts`, so a member-level run
669
669
  re-synths the entire workspace from the member's directory. Run `bun run sync`
670
670
  from the root instead.
671
671
 
672
+ ## Commit a registry-neutral lockfile
673
+
674
+ The root `bun.lock` is committed, and generated CI installs run
675
+ `bun install --frozen-lockfile`. A private npm mirror would otherwise appear in
676
+ every resolved tarball URL, so the generated `bun-lockfile` task runs after each
677
+ root install and as the root `postinstall` script, which also covers `bun add`,
678
+ `bun remove`, and `bun update`. It detects the active registry from the same
679
+ environment, `.npmrc`, `bunfig.toml`, and pnpm configuration a package manager
680
+ reads. When that registry is not public npm, it rewrites every tarball URL under
681
+ it and leaves all other URLs unchanged. Bun 1.4.2 has no native setting that
682
+ omits registry URLs from `bun.lock`.
683
+
684
+ The default replacement is an empty tarball field, which Bun resolves through
685
+ the installing machine's registry; integrity hashes still pin the content.
686
+ Run `bun run bun-lockfile -- --check` to confirm a lockfile is clean without
687
+ writing it. Options can also come from `PROJEN_BUN_LOCKFILE_*` environment
688
+ variables or `.env`.
689
+
672
690
  ## Manage workspace versions
673
691
 
674
692
  `VERSION` is the language-neutral version seam. Real package manifests remain
package/index.ts CHANGED
@@ -3,10 +3,11 @@
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.173";
6
+ export const PACKAGE_VERSION = "0.9.175";
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";
10
+ export * as bunLockfile from "./src/bun-lockfile.ts";
10
11
  export * as bunWorkflow from "./src/bun-workflow.ts";
11
12
  export * as clean from "./src/clean.ts";
12
13
  export * as codegen from "./src/codegen.ts";
@@ -48,6 +49,8 @@ export type { AppKitWorkspaceMode, AppKitBundleOptions, AppKitWorkspaceOptions }
48
49
  export { isGeneratedBarrel, folderBarrelFile, generateBarrels } from "./src/barrels.ts";
49
50
  export { BUN_DEV_OVERRIDE, BUN_BUILD_OVERRIDE, BUN_APP_OVERRIDES, RootBunfigFile, BunfigFile, BunDevServerFile, BunBuildFile } from "./src/bun-app.ts";
50
51
  export type { BunBuildFileOptions } from "./src/bun-app.ts";
52
+ export { BUN_LOCKFILE, BunLockfileReplacementSchema, BunLockfileOptionsSchema, bunLockfileRegistries, normalizeBunLockfile, normalizeBunLockfileAt } from "./src/bun-lockfile.ts";
53
+ export type { BunLockfileOptions, BunLockfileOptionsInput, NormalizedBunLockfile, BunLockfileNormalization } from "./src/bun-lockfile.ts";
51
54
  export { BUN_VERSION, MINIMUM_BUN_VERSION, BUN_VERSION_CHECK_SCRIPT } from "./src/bun-workflow.ts";
52
55
  export { listGeneratedFiles, listNodeModulesDirs, removePaths } from "./src/clean.ts";
53
56
  export { codegenModulePaths, generateCodegen } from "./src/codegen.ts";
@@ -68,7 +71,7 @@ export { resolveRepoRoot, repoRoot, DEFAULT_PACKAGE_ROOTS, projectName, toPosix,
68
71
  export type { RecordedPackage } from "./src/packages.ts";
69
72
  export { PnpmWorkspaceState } from "./src/pnpm-workspace.ts";
70
73
  export type { Catalog, AllowBuilds, DBXToolsPNPMWorkspaceOptions } from "./src/pnpm-workspace.ts";
71
- export { PackageIdentifier, identifier, applyCompilerOptions, applyIncludes, applyExports, addExports, addOptionalPeer, projectRepositoryUrl, projectReleaseBranch, addPackageFiles, srcFolderExports, LICENSE, DBXToolsNodeProject, ROOT_INSTALL_ONLY_MIXIN, DBXToolsTypeScriptProject, taskCommand } from "./src/project-js.ts";
74
+ export { PackageIdentifier, identifier, applyCompilerOptions, applyIncludes, applyExports, addExports, addOptionalPeer, projectRepositoryUrl, projectReleaseBranch, addPackageFiles, srcFolderExports, LICENSE, BUN_LOCKFILE_TASK, DBXToolsNodeProject, ROOT_INSTALL_ONLY_MIXIN, DBXToolsTypeScriptProject, taskCommand } from "./src/project-js.ts";
72
75
  export type { DBXToolsJavaScriptProject, DBXToolsReleaseMode, DiscoveredTypeScriptPackageOptions, DBXToolsJavaScriptProjectOptions, DBXToolsTypeScriptProjectOptions } from "./src/project-js.ts";
73
76
  export { isProject, isDBXToolsProject, isDBXToolsJavaScriptProject, hasName, hasIdentifierPackageName, hasIdentifierName, hasIdentifierScope, hasTag, hasPath } from "./src/project-predicate.ts";
74
77
  export { pythonPackagePath, pythonModuleName, DBXToolsPythonProject, DBXToolsPythonWorkspace } from "./src/project-py.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.173",
34
- "@dbx-tools/cli-args": "^0.9.173",
35
- "@dbx-tools/core": "^0.9.173",
36
- "@dbx-tools/path": "^0.9.173",
37
- "@dbx-tools/shared-core": "^0.9.173",
33
+ "@dbx-tools/auth": "^0.9.175",
34
+ "@dbx-tools/cli-args": "^0.9.175",
35
+ "@dbx-tools/core": "^0.9.175",
36
+ "@dbx-tools/path": "^0.9.175",
37
+ "@dbx-tools/shared-core": "^0.9.175",
38
38
  "commander": "^15.0.0",
39
39
  "concurrently": "^10.0.3",
40
40
  "constructs": "^10.6.0",
@@ -56,7 +56,7 @@
56
56
  },
57
57
  "main": "index.ts",
58
58
  "license": "Apache-2.0",
59
- "version": "0.9.173",
59
+ "version": "0.9.175",
60
60
  "types": "index.ts",
61
61
  "type": "module",
62
62
  "exports": {
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Registry-neutral Bun text lockfiles.
3
+ *
4
+ * Bun records each package's resolved tarball URL in `bun.lock`. When the
5
+ * installing machine's npm registry is a private mirror, every URL names that
6
+ * mirror, so a committed lockfile would leak it and fail on machines that cannot
7
+ * reach it. Normalization detects the active registry the same way a package
8
+ * manager does and, when it is not public npm, rewrites every tarball URL under
9
+ * it in bulk. All other URLs are left as they are.
10
+ *
11
+ * Bun accepts both replacement forms under `--frozen-lockfile`: an empty tarball
12
+ * field resolves through whichever registry the installing machine configures,
13
+ * and a public npm URL pins npmjs explicitly. Integrity hashes are unchanged, so
14
+ * frozen installs still verify the exact tarball content.
15
+ *
16
+ * Bun has no native setting for this as of 1.4.2: `[install.lockfile]` only
17
+ * supports `save` and `print`, and Bun's own `BunLockFile` type marks the npm
18
+ * registry field "TODO: remove if default". Replace this rewrite with that
19
+ * native behavior once Bun ships it.
20
+ */
21
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
22
+ import { join } from "node:path";
23
+ import { npmRegistry } from "@dbx-tools/core";
24
+ import { z } from "zod";
25
+
26
+ /** Bun's text lockfile at a workspace root. */
27
+ export const BUN_LOCKFILE = "bun.lock";
28
+
29
+ /** Public npm registry used by the `npmjs` replacement. */
30
+ const PUBLIC_NPM_REGISTRY = "https://registry.npmjs.org";
31
+
32
+ /** Registry tarball entry: `"key": ["name@version", "<tarball>", ...`. */
33
+ const TARBALL_ENTRY =
34
+ /^(\s+"[^"\n]+": \[")((?:@[^/"\n]+\/)?[^@"\n]+)@([^"\n]+)(", )"(https?:\/\/[^"\n]*)"/gm;
35
+
36
+ /** How private registry tarball URLs are rewritten. */
37
+ export const BunLockfileReplacementSchema = z
38
+ .enum(["empty", "npmjs"])
39
+ .describe(
40
+ "Replacement for private registry tarball URLs: empty resolves through the installing machine's registry; npmjs pins public npm",
41
+ );
42
+
43
+ /** Options that control `bun.lock` normalization. */
44
+ export const BunLockfileOptionsSchema = z
45
+ .object({
46
+ mode: z
47
+ .enum(["auto", "off"])
48
+ .default("auto")
49
+ .describe(
50
+ "auto rewrites tarball URLs under the detected npm registry when it is not public npm; off leaves the lockfile unchanged",
51
+ ),
52
+ replacement: BunLockfileReplacementSchema.default("empty"),
53
+ registries: z
54
+ .array(z.string().trim().url())
55
+ .default([])
56
+ .describe("Additional registry URLs whose tarball URLs are rewritten"),
57
+ check: z
58
+ .boolean()
59
+ .default(false)
60
+ .describe("Fail instead of writing when the lockfile is not normalized"),
61
+ })
62
+ .describe("Keep bun.lock free of private npm registry URLs");
63
+
64
+ /** Parsed {@link BunLockfileOptionsSchema}. */
65
+ export type BunLockfileOptions = z.output<typeof BunLockfileOptionsSchema>;
66
+
67
+ /** Accepted input for {@link BunLockfileOptionsSchema}. */
68
+ export type BunLockfileOptionsInput = z.input<typeof BunLockfileOptionsSchema>;
69
+
70
+ /** Result of normalizing one lockfile body. */
71
+ export interface NormalizedBunLockfile {
72
+ /** Lockfile text with private registry tarball URLs rewritten. */
73
+ readonly content: string;
74
+ /** Number of tarball URLs rewritten. */
75
+ readonly rewritten: number;
76
+ /** Registry prefixes that were rewritten. */
77
+ readonly registries: readonly string[];
78
+ }
79
+
80
+ /** URL origin plus path prefix of a registry, without a trailing slash. */
81
+ function registryPrefix(url: string): string | undefined {
82
+ try {
83
+ const parsed = new URL(url);
84
+ return `${parsed.origin}${parsed.pathname.replace(/\/+$/, "")}`;
85
+ } catch {
86
+ return undefined;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Registry URLs to rewrite for `cwd`.
92
+ *
93
+ * `auto` adds the active npm registry only when it is not public npm, so a
94
+ * machine on npmjs leaves the lockfile untouched.
95
+ */
96
+ export function bunLockfileRegistries(
97
+ cwd: string,
98
+ options: Pick<BunLockfileOptions, "mode" | "registries">,
99
+ ): string[] {
100
+ if (options.mode === "off") return [];
101
+ const detected = npmRegistry(cwd, { overrideOnly: true })?.href;
102
+ return [...(detected ? [detected] : []), ...options.registries];
103
+ }
104
+
105
+ /** Public npm tarball URL for one package version. */
106
+ function npmjsTarball(name: string, version: string): string {
107
+ const base = name.slice(name.lastIndexOf("/") + 1);
108
+ return `${PUBLIC_NPM_REGISTRY}/${name}/-/${base}-${version}.tgz`;
109
+ }
110
+
111
+ /** Rewrite tarball URLs under any of `registries`; leave every other URL as is. */
112
+ export function normalizeBunLockfile(
113
+ content: string,
114
+ registries: readonly string[],
115
+ replacement: z.output<typeof BunLockfileReplacementSchema> = "empty",
116
+ ): NormalizedBunLockfile {
117
+ const prefixes = [...new Set(registries.flatMap((registry) => registryPrefix(registry) ?? []))];
118
+ if (prefixes.length === 0) return { content, rewritten: 0, registries: prefixes };
119
+ let rewritten = 0;
120
+ const normalized = content.replace(
121
+ TARBALL_ENTRY,
122
+ (entry, head: string, name: string, version: string, separator: string, url: string) => {
123
+ if (!prefixes.some((prefix) => url.startsWith(`${prefix}/`))) return entry;
124
+ rewritten += 1;
125
+ const tarball = replacement === "npmjs" ? npmjsTarball(name, version) : "";
126
+ return `${head}${name}@${version}${separator}"${tarball}"`;
127
+ },
128
+ );
129
+ return { content: normalized, rewritten, registries: prefixes };
130
+ }
131
+
132
+ /** Outcome of {@link normalizeBunLockfileAt}. */
133
+ export interface BunLockfileNormalization extends NormalizedBunLockfile {
134
+ /** Absolute lockfile path. */
135
+ readonly path: string;
136
+ /** Whether the lockfile exists. */
137
+ readonly exists: boolean;
138
+ /** Whether the file content differs from its normalized form. */
139
+ readonly changed: boolean;
140
+ }
141
+
142
+ /** Normalize `bun.lock` in `root`, writing it unless `check` is set. */
143
+ export function normalizeBunLockfileAt(
144
+ root: string,
145
+ input: BunLockfileOptionsInput = {},
146
+ ): BunLockfileNormalization {
147
+ const options = BunLockfileOptionsSchema.parse(input);
148
+ const path = join(root, BUN_LOCKFILE);
149
+ if (!existsSync(path)) {
150
+ return { path, exists: false, changed: false, content: "", rewritten: 0, registries: [] };
151
+ }
152
+ const original = readFileSync(path, "utf8");
153
+ const result = normalizeBunLockfile(
154
+ original,
155
+ bunLockfileRegistries(root, options),
156
+ options.replacement,
157
+ );
158
+ const changed = result.content !== original;
159
+ if (changed && !options.check) writeFileSync(path, result.content);
160
+ return { ...result, path, exists: true, changed };
161
+ }
package/src/project-js.ts CHANGED
@@ -32,6 +32,7 @@ import {
32
32
  import { folderBarrelFile, generateBarrels } from "./barrels.ts";
33
33
 
34
34
  import { BUN_APP_OVERRIDES, RootBunfigFile } from "./bun-app.ts";
35
+ import { BUN_LOCKFILE } from "./bun-lockfile.ts";
35
36
  import { BUN_VERSION, BUN_VERSION_CHECK_SCRIPT, MINIMUM_BUN_VERSION } from "./bun-workflow.ts";
36
37
  import { codegenModulePaths, generateCodegen } from "./codegen.ts";
37
38
  import { DBXToolsConfig, type DBXToolsConfigOptions } from "./dbx-tools-config.ts";
@@ -527,6 +528,33 @@ const DEV_DEPS_ROOT: string[] = [
527
528
  "@types/node@^24.6.0",
528
529
  ];
529
530
 
531
+ /** Root task that clears registry tarball URLs from `bun.lock`. */
532
+ export const BUN_LOCKFILE_TASK = "bun-lockfile";
533
+
534
+ /**
535
+ * Commit `bun.lock` without leaking the installing machine's registry.
536
+ *
537
+ * Bun writes each resolved tarball URL into the lockfile. The generated
538
+ * `bun-lockfile` task detects the active npm registry (resolved like a package
539
+ * manager does, never hard-coded) and, when it is not public npm, rewrites every
540
+ * tarball URL under it; all other URLs stay as they are. It runs after every
541
+ * root install and as the root `postinstall` script, so `bun add`, `bun remove`,
542
+ * and `bun update` normalize too. CI installs with `--frozen-lockfile`.
543
+ */
544
+ function configureRootLockfile(project: DBXToolsJavaScriptProject): void {
545
+ const normalize = taskCommand(`${BUN_LOCKFILE_TASK}.ts`);
546
+ const task = project.addTask(BUN_LOCKFILE_TASK, {
547
+ description: "Rewrite private npm registry tarball URLs in bun.lock",
548
+ execArgs: normalize,
549
+ receiveArgs: true,
550
+ });
551
+ project.package.installTask.reset("bun install");
552
+ project.package.installTask.spawn(task);
553
+ project.package.installCiTask.reset(project.package.installCommand);
554
+ project.package.setScript("postinstall", normalize.join(" "));
555
+ project.annotateGenerated(`/${BUN_LOCKFILE}`);
556
+ }
557
+
530
558
  function configureRootBunRuntime(project: DBXToolsJavaScriptProject): void {
531
559
  new TextFile(project, BUN_VERSION_CHECK_SCRIPT, {
532
560
  lines: dedent(// ============================================================================
@@ -1330,9 +1358,7 @@ function initProject(
1330
1358
  if (selfDep) project.addDevDeps(selfDep);
1331
1359
  project.addDevDeps(...DEV_DEPS_ROOT);
1332
1360
  configureRootPackage(project);
1333
- // Workspaces generated by this engine intentionally do not track local-registry
1334
- // lockfiles, so the CI-named install task must resolve from manifests as well.
1335
- project.package.installCiTask.reset("bun install");
1361
+ configureRootLockfile(project);
1336
1362
  configureRootBunRuntime(project);
1337
1363
  const roots =
1338
1364
  options.packageRoots ??
@@ -1367,6 +1393,9 @@ function initProject(
1367
1393
  project.gitignore.addPatterns(
1368
1394
  ...[...ignore.ignorePatterns({ test: false, dot: false, lock: true })],
1369
1395
  );
1396
+ // The root Bun text lockfile is committed in registry-neutral form; see
1397
+ // configureRootLockfile. Other lockfiles stay ignored.
1398
+ project.gitignore.include(`/${BUN_LOCKFILE}`);
1370
1399
  // The generic compiled-output pattern `**/lib/**` must not hide authored
1371
1400
  // modules beneath a package's `src/lib/`.
1372
1401
  project.gitignore.include("**/src/lib/**");
package/src/release.ts CHANGED
@@ -118,36 +118,57 @@ export function uvSetupStep(): JobStep {
118
118
  };
119
119
  }
120
120
 
121
- function refreshDocsRegistryDependencies(workflow: GithubWorkflow): void {
122
- const docs = workflow.getJob("deploy-docs") as Job | undefined;
123
- if (!docs) return;
124
- if (workflow.getJob(PUBLICATION_VERIFIER_JOB)) {
125
- workflow.updateJob("deploy-docs", {
121
+ const DOCS_JOB = "deploy-docs";
122
+
123
+ function publicationRegistryJobs(workflow: GithubWorkflow): string[] {
124
+ return Object.keys(workflow.jobs).filter(
125
+ (name) => name === "publish-node" || name.startsWith("publish-pypi-"),
126
+ );
127
+ }
128
+
129
+ function publisherPrerequisites(registryJobs: readonly string[]): string[] {
130
+ return registryJobs.map((name) => {
131
+ const output = name === "publish-node" ? "npm" : "pypi";
132
+ return `needs['build-release'].outputs.${output} != 'true' || needs['${name}'].result == 'success'`;
133
+ });
134
+ }
135
+
136
+ /**
137
+ * Deploy docs after selected publishers succeed, then run the availability
138
+ * verifier. Docs never consume registry availability, so the slow public
139
+ * availability poll must not delay their deployment.
140
+ */
141
+ function refreshReleaseOrdering(workflow: GithubWorkflow): void {
142
+ const registryJobs = publicationRegistryJobs(workflow);
143
+ const docs = workflow.getJob(DOCS_JOB) as Job | undefined;
144
+ if (docs) {
145
+ workflow.updateJob(DOCS_JOB, {
126
146
  ...docs,
127
- needs: ["build-release", PUBLICATION_VERIFIER_JOB],
147
+ needs: ["build-release", ...registryJobs],
128
148
  if: releaseCondition([
129
149
  "needs['build-release'].result == 'success'",
130
150
  "needs['build-release'].outputs.docs == 'true'",
131
- `needs['${PUBLICATION_VERIFIER_JOB}'].result == 'success' || needs['${PUBLICATION_VERIFIER_JOB}'].result == 'skipped'`,
151
+ ...publisherPrerequisites(registryJobs),
132
152
  ]),
133
153
  });
134
154
  }
155
+ const verifier = workflow.getJob(PUBLICATION_VERIFIER_JOB) as Job | undefined;
156
+ if (verifier) {
157
+ workflow.updateJob(PUBLICATION_VERIFIER_JOB, {
158
+ ...verifier,
159
+ needs: ["build-release", ...registryJobs, ...(docs ? [DOCS_JOB] : [])],
160
+ });
161
+ }
135
162
  }
136
163
 
137
164
  /** Keep documentation publication behind every configured package registry. */
138
165
  export function refreshReleaseDocsDependencies(project: DBXToolsJavaScriptProject): void {
139
166
  const workflow = releaseWorkflows.get(project);
140
167
  if (workflow) {
141
- refreshDocsRegistryDependencies(workflow);
168
+ refreshReleaseOrdering(workflow);
142
169
  }
143
170
  }
144
171
 
145
- function publicationRegistryJobs(workflow: GithubWorkflow): string[] {
146
- return Object.keys(workflow.jobs).filter(
147
- (name) => name === "publish-node" || name.startsWith("publish-pypi-"),
148
- );
149
- }
150
-
151
172
  /** Keep one public availability barrier behind every configured registry publisher. */
152
173
  export function refreshPublicationVerification(project: DBXToolsJavaScriptProject): void {
153
174
  const workflow = releaseWorkflows.get(project);
@@ -160,10 +181,7 @@ export function refreshPublicationVerification(project: DBXToolsJavaScriptProjec
160
181
  ...(hasNpm ? ["needs['build-release'].outputs.npm == 'true'"] : []),
161
182
  ...(hasPypi ? ["needs['build-release'].outputs.pypi == 'true'"] : []),
162
183
  ].join(" || ");
163
- const prerequisites = registryJobs.map((name) => {
164
- const output = name === "publish-node" ? "npm" : "pypi";
165
- return `needs['build-release'].outputs.${output} != 'true' || needs['${name}'].result == 'success'`;
166
- });
184
+ const prerequisites = publisherPrerequisites(registryJobs);
167
185
  const job: Job = {
168
186
  if: releaseCondition([
169
187
  "needs['build-release'].result == 'success'",
@@ -240,7 +258,7 @@ export function refreshPublicationVerification(project: DBXToolsJavaScriptProjec
240
258
  } else {
241
259
  workflow.addJob(PUBLICATION_VERIFIER_JOB, job);
242
260
  }
243
- refreshDocsRegistryDependencies(workflow);
261
+ refreshReleaseOrdering(workflow);
244
262
  }
245
263
 
246
264
  function releaseBuildJob(
@@ -276,7 +294,7 @@ function releaseBuildJob(
276
294
  fetchDepth: 0,
277
295
  },
278
296
  }),
279
- ...project.renderWorkflowSetup({ mutable: true }),
297
+ ...project.renderWorkflowSetup({ mutable: false }),
280
298
  {
281
299
  name: "Verify release context",
282
300
  id: "release",
@@ -398,7 +416,7 @@ function nodePublishJob(): Job {
398
416
  }
399
417
 
400
418
  function addDocsJobs(workflow: GithubWorkflow): void {
401
- workflow.addJob("deploy-docs", {
419
+ workflow.addJob(DOCS_JOB, {
402
420
  if: releaseCondition([
403
421
  "needs['build-release'].result == 'success'",
404
422
  "needs['build-release'].outputs.docs == 'true'",
@@ -419,7 +437,7 @@ function addDocsJobs(workflow: GithubWorkflow): void {
419
437
  },
420
438
  ],
421
439
  });
422
- refreshDocsRegistryDependencies(workflow);
440
+ refreshReleaseOrdering(workflow);
423
441
  }
424
442
 
425
443
  /** Owns the single release workflow and local release preparation tasks. */
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env -S bun
2
+ /**
3
+ * Keep `bun.lock` free of private npm registry URLs.
4
+ *
5
+ * Detects the active npm registry and, when it is not public npm, rewrites
6
+ * every tarball URL under it. Options also read `PROJEN_BUN_LOCKFILE_*`
7
+ * environment variables and `.env`.
8
+ */
9
+ import { relative } from "node:path";
10
+ import { log } from "@dbx-tools/shared-core";
11
+ import { runTaskMain, taskCommand, taskOptions, taskRoot } from "./cli.ts";
12
+ import { TaskRootOptionSchema } from "./options.ts";
13
+ import { BunLockfileOptionsSchema, normalizeBunLockfileAt } from "../src/bun-lockfile.ts";
14
+
15
+ const logger = log.logger("projen:bun-lockfile");
16
+
17
+ const TaskSchema = BunLockfileOptionsSchema.extend({ root: TaskRootOptionSchema });
18
+
19
+ export async function main(args: string[] = process.argv.slice(2)): Promise<void> {
20
+ const { root, ...options } = await taskOptions(
21
+ taskCommand(import.meta.url, "Keep bun.lock free of private npm registry URLs", TaskSchema),
22
+ TaskSchema,
23
+ args,
24
+ );
25
+ const result = normalizeBunLockfileAt(taskRoot(root), options);
26
+ if (!result.exists || !result.changed) return;
27
+ const file = relative(process.cwd(), result.path) || result.path;
28
+ if (options.check) {
29
+ throw new Error(
30
+ `${file} contains ${result.rewritten} private registry tarball URLs; run \`bun run bun-lockfile\``,
31
+ );
32
+ }
33
+ logger.info(`rewrote ${result.rewritten} private registry tarball URLs`, { file });
34
+ }
35
+
36
+ await runTaskMain(import.meta, main);