@smoothbricks/cli 0.1.0 → 0.2.0

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 (148) hide show
  1. package/README.md +338 -88
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +136 -22
  4. package/dist/devenv/index.d.ts +10 -0
  5. package/dist/devenv/index.d.ts.map +1 -0
  6. package/dist/devenv/index.js +27 -0
  7. package/dist/lib/cli-package.d.ts +4 -0
  8. package/dist/lib/cli-package.d.ts.map +1 -0
  9. package/dist/lib/cli-package.js +15 -0
  10. package/dist/lib/devenv.d.ts +6 -0
  11. package/dist/lib/devenv.d.ts.map +1 -0
  12. package/dist/lib/devenv.js +82 -0
  13. package/dist/lib/run.d.ts +5 -0
  14. package/dist/lib/run.d.ts.map +1 -1
  15. package/dist/lib/run.js +13 -0
  16. package/dist/lib/workspace.d.ts +20 -1
  17. package/dist/lib/workspace.d.ts.map +1 -1
  18. package/dist/lib/workspace.js +87 -7
  19. package/dist/monorepo/commit-msg.d.ts +8 -1
  20. package/dist/monorepo/commit-msg.d.ts.map +1 -1
  21. package/dist/monorepo/commit-msg.js +85 -7
  22. package/dist/monorepo/index.d.ts +24 -2
  23. package/dist/monorepo/index.d.ts.map +1 -1
  24. package/dist/monorepo/index.js +136 -15
  25. package/dist/monorepo/lockfile.d.ts +5 -1
  26. package/dist/monorepo/lockfile.d.ts.map +1 -1
  27. package/dist/monorepo/lockfile.js +45 -5
  28. package/dist/monorepo/managed-files.d.ts +1 -1
  29. package/dist/monorepo/managed-files.d.ts.map +1 -1
  30. package/dist/monorepo/managed-files.js +28 -11
  31. package/dist/monorepo/nx-sync.d.ts +2 -1
  32. package/dist/monorepo/nx-sync.d.ts.map +1 -1
  33. package/dist/monorepo/nx-sync.js +8 -2
  34. package/dist/monorepo/package-hygiene.d.ts +6 -2
  35. package/dist/monorepo/package-hygiene.d.ts.map +1 -1
  36. package/dist/monorepo/package-hygiene.js +12 -4
  37. package/dist/monorepo/package-policy.d.ts +25 -2
  38. package/dist/monorepo/package-policy.d.ts.map +1 -1
  39. package/dist/monorepo/package-policy.js +1289 -27
  40. package/dist/monorepo/packed-manifest.d.ts +4 -0
  41. package/dist/monorepo/packed-manifest.d.ts.map +1 -0
  42. package/dist/monorepo/packed-manifest.js +51 -0
  43. package/dist/monorepo/packed-package.d.ts +3 -0
  44. package/dist/monorepo/packed-package.d.ts.map +1 -1
  45. package/dist/monorepo/packed-package.js +69 -31
  46. package/dist/monorepo/packs/index.d.ts +20 -1
  47. package/dist/monorepo/packs/index.d.ts.map +1 -1
  48. package/dist/monorepo/packs/index.js +240 -18
  49. package/dist/monorepo/publish-workflow.d.ts +82 -0
  50. package/dist/monorepo/publish-workflow.d.ts.map +1 -0
  51. package/dist/monorepo/publish-workflow.js +336 -0
  52. package/dist/monorepo/tool-validation.d.ts +22 -0
  53. package/dist/monorepo/tool-validation.d.ts.map +1 -0
  54. package/dist/monorepo/tool-validation.js +399 -0
  55. package/dist/nx/index.d.ts +24 -0
  56. package/dist/nx/index.d.ts.map +1 -0
  57. package/dist/nx/index.js +94 -0
  58. package/dist/nx-version-actions.cjs +25 -0
  59. package/dist/nx-version-actions.d.cts +6 -0
  60. package/dist/nx-version-actions.d.cts.map +1 -0
  61. package/dist/release/bootstrap-npm-packages.d.ts +19 -0
  62. package/dist/release/bootstrap-npm-packages.d.ts.map +1 -0
  63. package/dist/release/bootstrap-npm-packages.js +58 -0
  64. package/dist/release/candidates.d.ts +11 -0
  65. package/dist/release/candidates.d.ts.map +1 -0
  66. package/dist/release/candidates.js +181 -0
  67. package/dist/release/core.d.ts +51 -0
  68. package/dist/release/core.d.ts.map +1 -0
  69. package/dist/release/core.js +97 -0
  70. package/dist/release/github-release.d.ts +46 -0
  71. package/dist/release/github-release.d.ts.map +1 -0
  72. package/dist/release/github-release.js +97 -0
  73. package/dist/release/index.d.ts +39 -5
  74. package/dist/release/index.d.ts.map +1 -1
  75. package/dist/release/index.js +763 -130
  76. package/dist/release/npm-auth.d.ts +16 -0
  77. package/dist/release/npm-auth.d.ts.map +1 -0
  78. package/dist/release/npm-auth.js +39 -0
  79. package/dist/release/orchestration.d.ts +49 -0
  80. package/dist/release/orchestration.d.ts.map +1 -0
  81. package/dist/release/orchestration.js +113 -0
  82. package/dist/release/publish-plan.d.ts +17 -0
  83. package/dist/release/publish-plan.d.ts.map +1 -0
  84. package/dist/release/publish-plan.js +15 -0
  85. package/dist/release/retag-unpublished.d.ts +34 -0
  86. package/dist/release/retag-unpublished.d.ts.map +1 -0
  87. package/dist/release/retag-unpublished.js +77 -0
  88. package/managed/raw/git-format-staged.yml +54 -0
  89. package/managed/raw/tooling/git-hooks/commit-msg.sh +1 -1
  90. package/managed/raw/tooling/git-hooks/pre-commit.sh +2 -7
  91. package/managed/templates/github/actions/setup-devenv/action.yml +1 -7
  92. package/managed/templates/github/workflows/ci.yml +3 -0
  93. package/package.json +37 -4
  94. package/src/cli.ts +193 -40
  95. package/src/devenv/index.test.ts +49 -0
  96. package/src/devenv/index.ts +38 -0
  97. package/src/lib/cli-package.ts +18 -0
  98. package/src/lib/devenv.test.ts +28 -0
  99. package/src/lib/devenv.ts +89 -0
  100. package/src/lib/run.ts +19 -0
  101. package/src/lib/workspace.ts +117 -7
  102. package/src/monorepo/__tests__/nx-version-actions.test.ts +75 -0
  103. package/src/monorepo/__tests__/publish-workflow.test.ts +322 -0
  104. package/src/monorepo/commit-msg.test.ts +45 -0
  105. package/src/monorepo/commit-msg.ts +107 -7
  106. package/src/monorepo/index.ts +181 -15
  107. package/src/monorepo/lockfile.ts +52 -7
  108. package/src/monorepo/managed-files.ts +43 -13
  109. package/src/monorepo/nx-sync.ts +9 -2
  110. package/src/monorepo/package-hygiene.test.ts +34 -0
  111. package/src/monorepo/package-hygiene.ts +26 -4
  112. package/src/monorepo/package-policy.test.ts +902 -0
  113. package/src/monorepo/package-policy.ts +1527 -25
  114. package/src/monorepo/packed-manifest.ts +67 -0
  115. package/src/monorepo/packed-package.ts +90 -31
  116. package/src/monorepo/packs/index.test.ts +189 -0
  117. package/src/monorepo/packs/index.ts +314 -19
  118. package/src/monorepo/publish-workflow.ts +422 -0
  119. package/src/monorepo/setup-test-tracing.test.ts +120 -0
  120. package/src/monorepo/tool-validation.test.ts +261 -0
  121. package/src/monorepo/tool-validation.ts +462 -0
  122. package/src/nx/index.test.ts +64 -0
  123. package/src/nx/index.ts +119 -0
  124. package/src/nx-version-actions.cts +36 -0
  125. package/src/release/__tests__/bootstrap-npm-packages.test.ts +127 -0
  126. package/src/release/__tests__/candidates.test.ts +217 -0
  127. package/src/release/__tests__/core-properties.test.ts +149 -0
  128. package/src/release/__tests__/core-scenarios.test.ts +187 -0
  129. package/src/release/__tests__/core.test.ts +73 -0
  130. package/src/release/__tests__/fixture-repo.test.ts +314 -0
  131. package/src/release/__tests__/github-release.test.ts +149 -0
  132. package/src/release/__tests__/helpers/fixture-repo.ts +114 -0
  133. package/src/release/__tests__/npm-auth.test.ts +120 -0
  134. package/src/release/__tests__/orchestration.test.ts +246 -0
  135. package/src/release/__tests__/publish-plan.test.ts +67 -0
  136. package/src/release/__tests__/retag-unpublished.test.ts +160 -0
  137. package/src/release/__tests__/trust-publisher.test.ts +133 -0
  138. package/src/release/bootstrap-npm-packages.ts +88 -0
  139. package/src/release/candidates.ts +225 -0
  140. package/src/release/core.ts +171 -0
  141. package/src/release/github-release.ts +134 -0
  142. package/src/release/index.ts +974 -138
  143. package/src/release/npm-auth.ts +93 -0
  144. package/src/release/orchestration.ts +196 -0
  145. package/src/release/publish-plan.ts +37 -0
  146. package/src/release/retag-unpublished.ts +122 -0
  147. package/managed/raw/.git-format-staged.yml +0 -47
  148. package/managed/templates/github/workflows/publish.yml +0 -136
package/dist/cli.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Command, CommanderError } from 'commander';
2
+ import { cliPackageVersion } from './lib/cli-package.js';
2
3
  import { findRepoRoot } from './lib/run.js';
3
4
  export async function runCli(argv = process.argv.slice(2)) {
4
5
  const program = buildProgram();
@@ -19,7 +20,12 @@ export async function runCli(argv = process.argv.slice(2)) {
19
20
  }
20
21
  function buildProgram() {
21
22
  const program = new Command();
22
- program.name('smoo').description('SmoothBricks monorepo tooling').exitOverride().showHelpAfterError();
23
+ program
24
+ .name('smoo')
25
+ .description('SmoothBricks monorepo tooling')
26
+ .version(cliPackageVersion, '-v, --version', 'print smoo version')
27
+ .exitOverride()
28
+ .showHelpAfterError();
23
29
  const monorepo = program.command('monorepo').description('Manage SmoothBricks-style monorepos');
24
30
  monorepo
25
31
  .command('init')
@@ -31,7 +37,10 @@ function buildProgram() {
31
37
  });
32
38
  monorepo
33
39
  .command('validate')
40
+ .option('--fix', 'apply safe monorepo policy fixes before validation')
34
41
  .option('--fail-fast', 'stop after the first failing validation pack')
42
+ .option('--only-if-new-workspace-package', 'skip validation unless a new workspace package manifest is staged')
43
+ .option('--verbose', 'print validation progress and successful checks')
35
44
  .action(async (options) => {
36
45
  const { validateMonorepo } = await import('./monorepo/index.js');
37
46
  await validateMonorepo(await findRepoRoot(), options);
@@ -48,70 +57,175 @@ function buildProgram() {
48
57
  const { diffManagedFiles } = await import('./monorepo/index.js');
49
58
  diffManagedFiles(await findRepoRoot());
50
59
  });
51
- monorepo.command('validate-commit-msg <commitMsgFile>').action(async (commitMsgFile) => {
60
+ monorepo
61
+ .command('validate-commit-msg <commitMsgFile>')
62
+ .option('--fix', 'format the commit message before validation')
63
+ .action(async (commitMsgFile, options) => {
52
64
  const { validateCommitMessageFile } = await import('./monorepo/index.js');
53
- validateCommitMessageFile(commitMsgFile);
65
+ validateCommitMessageFile(commitMsgFile, options, await findRepoRoot());
54
66
  });
55
67
  monorepo.command('sync-bun-lockfile-versions').action(async () => {
56
68
  const { syncBunLockfileVersions } = await import('./monorepo/index.js');
57
69
  syncBunLockfileVersions(await findRepoRoot());
58
70
  });
59
- monorepo.command('list-public-projects').action(async () => {
60
- const { listPublicProjects } = await import('./monorepo/index.js');
61
- console.log(listPublicProjects(await findRepoRoot()));
71
+ monorepo
72
+ .command('list-release-packages')
73
+ .option('--fail-empty', 'fail when no owned release packages are found')
74
+ .option('--github-output <path>', 'append projects=<nx-projects> to a GitHub Actions output file')
75
+ .action(async (options) => {
76
+ const { listReleaseProjectNamesForNx } = await import('./monorepo/index.js');
77
+ const packages = listReleaseProjectNamesForNx(await findRepoRoot(), options);
78
+ if (!options.githubOutput) {
79
+ console.log(packages);
80
+ }
62
81
  });
63
82
  monorepo.command('validate-public-tags').action(async () => {
64
83
  const { validatePublicPackageTags } = await import('./monorepo/index.js');
65
84
  validatePublicPackageTags(await findRepoRoot());
66
85
  });
67
- monorepo.command('release-state').action(async () => {
86
+ monorepo
87
+ .command('setup-test-tracing')
88
+ .description('Configure LMAO Bun test tracing for workspace packages')
89
+ .option('--all', 'configure every workspace package')
90
+ .option('--projects <projects>', 'comma-separated Nx project names, package names, or package roots')
91
+ .option('--op-context-export <exportName>', 'named op context export imported by test-suite-tracer', 'opContext')
92
+ .option('--tracer-module <module>', 'module specifier that exports defineTestTracer', '@smoothbricks/lmao/testing/bun')
93
+ .option('--dry-run', 'print generator invocations without writing files')
94
+ .action(async (options) => {
95
+ const { setupTestTracing } = await import('./monorepo/index.js');
96
+ await setupTestTracing(await findRepoRoot(), options);
97
+ });
98
+ const release = program.command('release').description('Version, publish, and create GitHub Releases');
99
+ release.command('npm-status').action(async () => {
68
100
  const { printReleaseState } = await import('./release/index.js');
69
101
  await printReleaseState(await findRepoRoot());
70
102
  });
71
- const release = program.command('release').description('Version, publish, and create GitHub Releases');
103
+ release
104
+ .command('repair-pending')
105
+ .description('Repair incomplete older release commits before releasing the current HEAD')
106
+ .option('--dry-run [dryRun]', 'run without pushing, publishing, or writing GitHub Releases')
107
+ .action(async (options) => {
108
+ const { releaseRepairPending } = await import('./release/index.js');
109
+ await releaseRepairPending(await findRepoRoot(), { ...options, dryRun: booleanOption(options.dryRun) });
110
+ });
72
111
  release
73
112
  .command('version')
74
113
  .option('--bump <bump>', 'auto, patch, minor, major, or prerelease', 'auto')
75
- .option('--dry-run [dryRun]', 'run without writing versions, tags, or pushes')
114
+ .option('--dry-run [dryRun]', 'run without writing versions or tags')
115
+ .option('--github-output <path>', 'append mode=<mode> and projects=<nx-projects> to a GitHub Actions output file')
76
116
  .action(async (options) => {
77
117
  const { releaseVersion } = await import('./release/index.js');
78
- await releaseVersion(await findRepoRoot(), { bump: options.bump, dryRun: booleanOption(options.dryRun) });
118
+ await releaseVersion(await findRepoRoot(), {
119
+ bump: options.bump,
120
+ dryRun: booleanOption(options.dryRun),
121
+ githubOutput: options.githubOutput,
122
+ });
79
123
  });
80
124
  release
81
125
  .command('publish')
82
126
  .option('--bump <bump>', 'auto, patch, minor, major, or prerelease', 'auto')
83
- .option('--tag <tag>', 'explicit npm dist-tag; must match bump-derived tag')
84
- .option('--npm-tag <npmTag>', 'explicit npm dist-tag; must match bump-derived tag')
85
- .option('--dry-run [dryRun]', 'run without publishing')
127
+ .option('--dry-run [dryRun]', 'run without pushing, publishing, or writing GitHub Releases')
86
128
  .action(async (options) => {
87
129
  const { releasePublish } = await import('./release/index.js');
88
130
  await releasePublish(await findRepoRoot(), { ...options, dryRun: booleanOption(options.dryRun) });
89
131
  });
90
132
  release
91
- .command('github-release')
92
- .option('--tags <tags>', 'space-separated release tags')
93
- .option('--bump <bump>', 'auto, patch, minor, major, or prerelease', 'auto')
94
- .option('--tag <tag>', 'explicit npm dist-tag; must match bump-derived tag')
95
- .option('--npm-tag <npmTag>', 'explicit npm dist-tag; must match bump-derived tag')
96
- .option('--dry-run [dryRun]', 'skip GitHub Release writes')
133
+ .command('retag-unpublished')
134
+ .description('Move unpublished owned release tags to a later commit without bumping versions')
135
+ .argument('<tag...>', 'owned release tags to move, for example @scope/pkg@1.2.3')
136
+ .option('--to <ref>', 'commit or ref to move tags to', 'HEAD')
137
+ .option('--push', 'push moved tags with force-with-lease')
138
+ .option('--dispatch', 'push moved tags and start publish.yml with bump=auto')
139
+ .option('--remote <remote>', 'git remote used for pushed tags')
140
+ .option('--branch <branch>', 'branch used for publish workflow dispatch')
141
+ .option('--dry-run [dryRun]', 'validate and print the retag operation without mutating refs')
142
+ .action(async (tags, options) => {
143
+ const { releaseRetagUnpublished } = await import('./release/index.js');
144
+ await releaseRetagUnpublished(await findRepoRoot(), {
145
+ tags,
146
+ to: options.to,
147
+ push: options.push === true,
148
+ dispatch: options.dispatch === true,
149
+ remote: options.remote,
150
+ branch: options.branch,
151
+ dryRun: booleanOption(options.dryRun),
152
+ });
153
+ });
154
+ release
155
+ .command('bootstrap-npm-packages')
156
+ .alias('bootstrap')
157
+ .description('Publish minimal npm placeholder packages so trusted publishing can be configured')
158
+ .option('--dry-run [dryRun]', 'show placeholder publishes without logging in or publishing')
159
+ .option('--skip-login', 'skip npm browser login before publishing placeholders')
160
+ .option('--otp <otp>', 'npm one-time password for placeholder publish operations')
161
+ .option('--package <name...>', 'only bootstrap the selected owned release package names')
97
162
  .action(async (options) => {
98
- const { releaseGithubRelease } = await import('./release/index.js');
99
- await releaseGithubRelease(await findRepoRoot(), { ...options, dryRun: booleanOption(options.dryRun) });
163
+ const { releaseBootstrapNpmPackages } = await import('./release/index.js');
164
+ await releaseBootstrapNpmPackages(await findRepoRoot(), {
165
+ dryRun: booleanOption(options.dryRun),
166
+ skipLogin: options.skipLogin === true,
167
+ otp: options.otp,
168
+ packages: options.package ?? [],
169
+ });
100
170
  });
101
171
  release
102
172
  .command('trust-publisher')
103
- .description('Configure npm trusted publishing for public packages')
173
+ .description('Configure npm trusted publishing for owned release packages')
104
174
  .option('--dry-run [dryRun]', 'show npm trust changes without saving them')
175
+ .option('--bootstrap', 'publish missing npm placeholder packages before configuring trust')
105
176
  .option('--otp <otp>', 'npm one-time password for trust operations')
177
+ .option('--bootstrap-otp <otp>', 'npm one-time password for placeholder publishes during --bootstrap')
106
178
  .option('--skip-login', 'skip npm browser login before configuring trust')
107
179
  .action(async (options) => {
108
180
  const { releaseTrustPublisher } = await import('./release/index.js');
109
181
  await releaseTrustPublisher(await findRepoRoot(), {
110
182
  dryRun: booleanOption(options.dryRun),
183
+ bootstrap: options.bootstrap === true,
111
184
  otp: options.otp,
185
+ bootstrapOtp: options.bootstrapOtp,
112
186
  skipLogin: options.skipLogin === true,
113
187
  });
114
188
  });
189
+ const devenv = program.command('devenv').description('Manage the repository devenv shell');
190
+ devenv.command('update').action(async () => {
191
+ const { updateDevenv } = await import('./devenv/index.js');
192
+ await updateDevenv(await findRepoRoot());
193
+ });
194
+ devenv.command('reload').action(async () => {
195
+ const { reloadDevenv } = await import('./devenv/index.js');
196
+ await reloadDevenv(await findRepoRoot());
197
+ });
198
+ const nixpkgsOverlay = program.command('nixpkgs-overlay').description('Manage the repository nixpkgs overlay');
199
+ nixpkgsOverlay.command('update').action(async () => {
200
+ const { updateNixpkgsOverlay } = await import('./devenv/index.js');
201
+ await updateNixpkgsOverlay(await findRepoRoot());
202
+ });
203
+ const nx = program.command('nx').description('Nx workspace helpers');
204
+ nx.command('list-targets')
205
+ .description('List project:target entries for every Nx project')
206
+ .action(async () => {
207
+ const { listTargets } = await import('./nx/index.js');
208
+ await listTargets(await findRepoRoot());
209
+ });
210
+ nx.command('list-projects')
211
+ .description('List Nx projects matching filters')
212
+ .requiredOption('--with-target <target>', 'only include projects defining this target')
213
+ .action(async (options) => {
214
+ const { listProjects } = await import('./nx/index.js');
215
+ await listProjects(await findRepoRoot(), options);
216
+ });
217
+ nx.command('reset-cache')
218
+ .description('Run nx reset to clear Nx daemon and cache state')
219
+ .action(async () => {
220
+ const { resetCache } = await import('./nx/index.js');
221
+ await resetCache(await findRepoRoot());
222
+ });
223
+ nx.command('clean-cache')
224
+ .description('Remove local Nx cache directories when present')
225
+ .action(async () => {
226
+ const { cleanCache } = await import('./nx/index.js');
227
+ await cleanCache(await findRepoRoot());
228
+ });
115
229
  const githubCi = program.command('github-ci').description('GitHub Actions helpers');
116
230
  githubCi.command('cleanup-cache').action(async () => {
117
231
  const { cleanupGithubCiCache } = await import('./github-ci/index.js');
@@ -0,0 +1,10 @@
1
+ export interface DevenvCommandShell {
2
+ run(command: string, args: string[], cwd: string): Promise<void>;
3
+ remove(path: string): Promise<void>;
4
+ }
5
+ export declare function updateDevenv(root: string, shell?: DevenvCommandShell): Promise<void>;
6
+ export declare function reloadDevenv(root: string, shell?: DevenvCommandShell): Promise<void>;
7
+ export declare function updateNixpkgsOverlay(root: string, shell?: DevenvCommandShell): Promise<void>;
8
+ export declare function direnvRoot(root: string): string;
9
+ export declare function nixpkgsOverlayRoot(root: string): string;
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/devenv/index.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,kBAAkB;IACjC,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AASD,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,kBAAiC,GAAG,OAAO,CAAC,IAAI,CAAC,CAExG;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,kBAAiC,GAAG,OAAO,CAAC,IAAI,CAAC,CAKxG;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,kBAAiC,GAAG,OAAO,CAAC,IAAI,CAAC,CAEhH;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD"}
@@ -0,0 +1,27 @@
1
+ import { rm } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { run } from '../lib/run.js';
4
+ const defaultShell = {
5
+ run,
6
+ async remove(path) {
7
+ await rm(path, { recursive: true, force: true });
8
+ },
9
+ };
10
+ export async function updateDevenv(root, shell = defaultShell) {
11
+ await shell.run('devenv', ['update'], direnvRoot(root));
12
+ }
13
+ export async function reloadDevenv(root, shell = defaultShell) {
14
+ const cwd = direnvRoot(root);
15
+ await shell.remove(join(cwd, '.direnv'));
16
+ await shell.remove(join(cwd, '.devenv'));
17
+ await shell.run('direnv', ['reload'], cwd);
18
+ }
19
+ export async function updateNixpkgsOverlay(root, shell = defaultShell) {
20
+ await shell.run('nix', ['shell', 'nixpkgs#nvfetcher', '-c', 'nvfetcher', '-o', '_sources'], nixpkgsOverlayRoot(root));
21
+ }
22
+ export function direnvRoot(root) {
23
+ return join(root, 'tooling', 'direnv');
24
+ }
25
+ export function nixpkgsOverlayRoot(root) {
26
+ return join(direnvRoot(root), 'nixpkgs-overlay');
27
+ }
@@ -0,0 +1,4 @@
1
+ export declare const smoothBricksCodebasePackageName = "@smoothbricks/codebase";
2
+ export declare const cliPackageVersion: string;
3
+ export declare function isSmoothBricksCodebasePackageName(name: string | undefined): boolean;
4
+ //# sourceMappingURL=cli-package.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-package.d.ts","sourceRoot":"","sources":["../../src/lib/cli-package.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,+BAA+B,2BAA2B,CAAC;AACxE,eAAO,MAAM,iBAAiB,QAA0B,CAAC;AAWzD,wBAAgB,iCAAiC,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAEnF"}
@@ -0,0 +1,15 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ import { readJsonObject, stringProperty } from './json.js';
3
+ export const smoothBricksCodebasePackageName = '@smoothbricks/codebase';
4
+ export const cliPackageVersion = readCliPackageVersion();
5
+ function readCliPackageVersion() {
6
+ const pkg = readJsonObject(fileURLToPath(new URL('../../package.json', import.meta.url)));
7
+ const version = pkg ? stringProperty(pkg, 'version') : null;
8
+ if (!version) {
9
+ throw new Error('Unable to read @smoothbricks/cli package version.');
10
+ }
11
+ return version;
12
+ }
13
+ export function isSmoothBricksCodebasePackageName(name) {
14
+ return name === smoothBricksCodebasePackageName;
15
+ }
@@ -0,0 +1,6 @@
1
+ type EnvSnapshot = Record<string, string>;
2
+ export declare function withDevenvEnv<T>(root: string, runWithEnv: () => Promise<T>): Promise<T>;
3
+ export declare function parseNulEnv(bytes: Uint8Array): EnvSnapshot;
4
+ export declare function mergeDevenvEnv(baseEnv: EnvSnapshot, devenvEnv: EnvSnapshot): EnvSnapshot;
5
+ export {};
6
+ //# sourceMappingURL=devenv.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"devenv.d.ts","sourceRoot":"","sources":["../../src/lib/devenv.ts"],"names":[],"mappings":"AAMA,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE1C,wBAAsB,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAS7F;AAmBD,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,WAAW,CAoB1D;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,GAAG,WAAW,CAExF"}
@@ -0,0 +1,82 @@
1
+ import { mkdtemp, readFile, rm } from 'node:fs/promises';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { $ } from 'bun';
5
+ import { decode } from './run.js';
6
+ export async function withDevenvEnv(root, runWithEnv) {
7
+ const snapshot = snapshotProcessEnv();
8
+ const env = await loadDevenvEnv(root);
9
+ replaceProcessEnv(mergeDevenvEnv(snapshot, env));
10
+ try {
11
+ return await runWithEnv();
12
+ }
13
+ finally {
14
+ restoreProcessEnv(snapshot);
15
+ }
16
+ }
17
+ async function loadDevenvEnv(root) {
18
+ const tempDir = await mkdtemp(join(tmpdir(), 'smoo-devenv-env-'));
19
+ const envPath = join(tempDir, 'env');
20
+ try {
21
+ const result = await $ `devenv shell -- bash -lc ${'env -0 > "$1"'} bash ${envPath}`
22
+ .cwd(join(root, 'tooling', 'direnv'))
23
+ .quiet()
24
+ .nothrow();
25
+ if (result.exitCode !== 0) {
26
+ throw new Error('devenv shell failed. Ensure devenv is installed and the tooling/direnv shell is valid.');
27
+ }
28
+ return parseNulEnv(await readFile(envPath));
29
+ }
30
+ finally {
31
+ await rm(tempDir, { recursive: true, force: true });
32
+ }
33
+ }
34
+ export function parseNulEnv(bytes) {
35
+ const env = {};
36
+ let entryStart = 0;
37
+ for (let index = 0; index <= bytes.length; index += 1) {
38
+ if (index !== bytes.length && bytes[index] !== 0) {
39
+ continue;
40
+ }
41
+ if (index === entryStart) {
42
+ entryStart = index + 1;
43
+ continue;
44
+ }
45
+ const entry = decode(bytes.subarray(entryStart, index));
46
+ const separator = entry.indexOf('=');
47
+ if (separator <= 0) {
48
+ throw new Error('devenv shell produced an invalid environment entry.');
49
+ }
50
+ env[entry.slice(0, separator)] = entry.slice(separator + 1);
51
+ entryStart = index + 1;
52
+ }
53
+ return env;
54
+ }
55
+ export function mergeDevenvEnv(baseEnv, devenvEnv) {
56
+ return { ...baseEnv, ...devenvEnv };
57
+ }
58
+ function snapshotProcessEnv() {
59
+ const snapshot = {};
60
+ for (const [key, value] of Object.entries(process.env)) {
61
+ if (value !== undefined) {
62
+ snapshot[key] = value;
63
+ }
64
+ }
65
+ return snapshot;
66
+ }
67
+ function replaceProcessEnv(env) {
68
+ for (const key of Object.keys(process.env)) {
69
+ delete process.env[key];
70
+ }
71
+ for (const [key, value] of Object.entries(env)) {
72
+ process.env[key] = value;
73
+ }
74
+ }
75
+ function restoreProcessEnv(snapshot) {
76
+ for (const key of Object.keys(process.env)) {
77
+ delete process.env[key];
78
+ }
79
+ for (const [key, value] of Object.entries(snapshot)) {
80
+ process.env[key] = value;
81
+ }
82
+ }
package/dist/lib/run.d.ts CHANGED
@@ -1,5 +1,10 @@
1
1
  export declare function run(command: string, args: string[], cwd: string, env?: Record<string, string>): Promise<void>;
2
2
  export declare function runStatus(command: string, args: string[], cwd: string, quiet?: boolean, env?: Record<string, string>): Promise<number>;
3
+ export declare function runResult(command: string, args: string[], cwd: string, env?: Record<string, string>): Promise<{
4
+ exitCode: number;
5
+ stdout: string;
6
+ stderr: string;
7
+ }>;
3
8
  export declare function findRepoRoot(): Promise<string>;
4
9
  export declare function decode(bytes: Uint8Array): string;
5
10
  //# sourceMappingURL=run.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/lib/run.ts"],"names":[],"mappings":"AAKA,wBAAsB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAKnH;AAED,wBAAsB,SAAS,CAC7B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,MAAM,EACX,KAAK,UAAQ,EACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC3B,OAAO,CAAC,MAAM,CAAC,CAQjB;AAuCD,wBAAsB,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAMpD;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAEhD"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/lib/run.ts"],"names":[],"mappings":"AAKA,wBAAsB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAKnH;AAED,wBAAsB,SAAS,CAC7B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,MAAM,EACX,KAAK,UAAQ,EACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC3B,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED,wBAAsB,SAAS,CAC7B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,MAAM,EACX,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC3B,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAY/D;AAuCD,wBAAsB,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAMpD;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAEhD"}
package/dist/lib/run.js CHANGED
@@ -17,6 +17,19 @@ export async function runStatus(command, args, cwd, quiet = false, env) {
17
17
  const result = quiet ? await shell.quiet() : await shell;
18
18
  return result.exitCode;
19
19
  }
20
+ export async function runResult(command, args, cwd, env) {
21
+ const invocation = resolveCommandInvocation(cwd, command, args);
22
+ let shell = $ `${invocation.command} ${invocation.args}`.cwd(cwd).nothrow().quiet();
23
+ if (env) {
24
+ shell = shell.env(mergeEnv(env));
25
+ }
26
+ const result = await shell;
27
+ return {
28
+ exitCode: result.exitCode,
29
+ stdout: decode(result.stdout),
30
+ stderr: decode(result.stderr),
31
+ };
32
+ }
20
33
  function mergeEnv(env) {
21
34
  const merged = {};
22
35
  for (const [key, value] of Object.entries(process.env)) {
@@ -1,5 +1,6 @@
1
1
  export interface PackageInfo {
2
2
  name: string;
3
+ projectName: string;
3
4
  version: string;
4
5
  private: boolean;
5
6
  tags: string[];
@@ -7,17 +8,35 @@ export interface PackageInfo {
7
8
  packageJsonPath: string;
8
9
  json: Record<string, unknown>;
9
10
  }
11
+ export interface PackageJson extends Record<string, unknown> {
12
+ name?: string;
13
+ version?: string;
14
+ private?: boolean;
15
+ workspaces?: unknown;
16
+ devDependencies?: unknown;
17
+ dependencies?: unknown;
18
+ nx?: unknown;
19
+ repository?: unknown;
20
+ }
10
21
  export interface RepositoryInfo {
11
22
  type: string;
12
23
  url: string;
13
24
  }
14
- export declare const workspaceDependencyFields: readonly ["dependencies", "devDependencies", "optionalDependencies"];
25
+ export declare const workspaceDependencyFields: readonly ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"];
15
26
  export declare function listPublicPackages(root: string): PackageInfo[];
27
+ export declare function listReleasePackages(root: string, rootPackage?: PackageInfo | null): PackageInfo[];
28
+ export declare function rootRepositoryInfo(root: string): RepositoryInfo | null;
29
+ export declare function rootPackageName(root: string): string | null;
30
+ export declare function isSmoothBricksCodebase(root: string): boolean;
31
+ export declare function packageRepositoryInfo(pkg: PackageInfo): RepositoryInfo | null;
32
+ export declare function isOwnedPackage(rootRepository: RepositoryInfo, pkg: PackageInfo): boolean;
16
33
  export declare function getWorkspacePackages(root: string): PackageInfo[];
17
34
  export declare function listPackageJsonRecords(root: string): PackageInfo[];
18
35
  export declare function getWorkspacePatterns(root: string): string[];
19
36
  export declare function readPackageJson(path: string): PackageInfo | null;
37
+ export declare function readPackageJsonObject(path: string): PackageJson | null;
20
38
  export declare function getNxTags(pkg: Record<string, unknown>): string[];
21
39
  export declare function repositoryInfo(pkg: Record<string, unknown>): RepositoryInfo | null;
40
+ export declare function sameRepositoryAfterNormalization(left: string, right: string): boolean;
22
41
  export declare function escapeRegex(value: string): string;
23
42
  //# sourceMappingURL=workspace.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../src/lib/workspace.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,eAAO,MAAM,yBAAyB,sEAAuE,CAAC;AAE9G,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAE9D;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAgChE;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAMlE;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAa3D;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAgBhE;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAKhE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,cAAc,GAAG,IAAI,CAalF;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEjD"}
1
+ {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../src/lib/workspace.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,WAAY,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC1D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,eAAO,MAAM,yBAAyB,0FAK5B,CAAC;AAEX,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAE9D;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,WAAW,qBAA8C,GACxD,WAAW,EAAE,CAWf;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAGtE;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG3D;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,WAAW,GAAG,cAAc,GAAG,IAAI,CAE7E;AAED,wBAAgB,cAAc,CAAC,cAAc,EAAE,cAAc,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAGxF;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAMhE;AAiCD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CASlE;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAG3D;AAgBD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAiBhE;AAOD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAGtE;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAKhE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,cAAc,GAAG,IAAI,CAalF;AAED,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAErF;AA4BD,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEjD"}
@@ -1,15 +1,52 @@
1
1
  import { readdirSync, statSync } from 'node:fs';
2
2
  import { dirname, join, relative } from 'node:path';
3
+ import { isSmoothBricksCodebasePackageName } from './cli-package.js';
3
4
  import { hasOwn, hasOwnString, isRecord, readJson, readJsonObject, stringProperty } from './json.js';
4
- export const workspaceDependencyFields = ['dependencies', 'devDependencies', 'optionalDependencies'];
5
+ export const workspaceDependencyFields = [
6
+ 'dependencies',
7
+ 'devDependencies',
8
+ 'peerDependencies',
9
+ 'optionalDependencies',
10
+ ];
5
11
  export function listPublicPackages(root) {
6
12
  return getWorkspacePackages(root).filter((pkg) => !pkg.private && pkg.tags.includes('npm:public'));
7
13
  }
14
+ export function listReleasePackages(root, rootPackage = readPackageJson(join(root, 'package.json'))) {
15
+ if (!rootPackage) {
16
+ return [];
17
+ }
18
+ const rootRepository = repositoryInfo(rootPackage.json);
19
+ if (!rootRepository) {
20
+ return [];
21
+ }
22
+ return getWorkspacePackagesForPatterns(root, getWorkspacePatternsFromPackageJson(rootPackage.json)).filter((pkg) => !pkg.private && pkg.tags.includes('npm:public') && isOwnedPackage(rootRepository, pkg));
23
+ }
24
+ export function rootRepositoryInfo(root) {
25
+ const rootPackage = readJsonObject(join(root, 'package.json'));
26
+ return rootPackage ? repositoryInfo(rootPackage) : null;
27
+ }
28
+ export function rootPackageName(root) {
29
+ const rootPackage = readPackageJsonObject(join(root, 'package.json'));
30
+ return rootPackage ? stringProperty(rootPackage, 'name') : null;
31
+ }
32
+ export function isSmoothBricksCodebase(root) {
33
+ return isSmoothBricksCodebasePackageName(rootPackageName(root) ?? undefined);
34
+ }
35
+ export function packageRepositoryInfo(pkg) {
36
+ return repositoryInfo(pkg.json);
37
+ }
38
+ export function isOwnedPackage(rootRepository, pkg) {
39
+ const packageRepository = packageRepositoryInfo(pkg);
40
+ return packageRepository !== null && packageRepository.url === rootRepository.url;
41
+ }
8
42
  export function getWorkspacePackages(root) {
9
- if (!readPackageJson(join(root, 'package.json'))) {
43
+ const rootPackage = readPackageJson(join(root, 'package.json'));
44
+ if (!rootPackage) {
10
45
  throw new Error('package.json not found or invalid');
11
46
  }
12
- const workspacePatterns = getWorkspacePatterns(root);
47
+ return getWorkspacePackagesForPatterns(root, getWorkspacePatternsFromPackageJson(rootPackage.json));
48
+ }
49
+ function getWorkspacePackagesForPatterns(root, workspacePatterns) {
13
50
  const packages = [];
14
51
  for (const pattern of workspacePatterns) {
15
52
  if (!pattern.endsWith('/*')) {
@@ -27,6 +64,7 @@ export function getWorkspacePackages(root) {
27
64
  }
28
65
  packages.push({
29
66
  name: pkg.name,
67
+ projectName: pkg.projectName,
30
68
  version: pkg.version,
31
69
  private: pkg.private,
32
70
  tags: pkg.tags,
@@ -43,14 +81,20 @@ export function listPackageJsonRecords(root) {
43
81
  if (!rootPackage) {
44
82
  throw new Error('package.json not found or invalid');
45
83
  }
46
- return [{ ...rootPackage, path: '.' }, ...getWorkspacePackages(root)];
84
+ return [
85
+ { ...rootPackage, path: '.' },
86
+ ...getWorkspacePackagesForPatterns(root, getWorkspacePatternsFromPackageJson(rootPackage.json)),
87
+ ];
47
88
  }
48
89
  export function getWorkspacePatterns(root) {
49
90
  const raw = readJson(join(root, 'package.json'));
50
- if (!isRecord(raw) || !hasOwn(raw, 'workspaces')) {
91
+ return isRecord(raw) ? getWorkspacePatternsFromPackageJson(raw) : ['packages/*'];
92
+ }
93
+ function getWorkspacePatternsFromPackageJson(pkg) {
94
+ if (!hasOwn(pkg, 'workspaces')) {
51
95
  return ['packages/*'];
52
96
  }
53
- const workspaces = raw.workspaces;
97
+ const workspaces = pkg.workspaces;
54
98
  if (Array.isArray(workspaces)) {
55
99
  return workspaces.filter((entry) => typeof entry === 'string');
56
100
  }
@@ -60,7 +104,7 @@ export function getWorkspacePatterns(root) {
60
104
  return ['packages/*'];
61
105
  }
62
106
  export function readPackageJson(path) {
63
- const parsed = readJsonObject(path);
107
+ const parsed = readPackageJsonObject(path);
64
108
  if (!isRecord(parsed) || !hasOwnString(parsed, 'name') || !hasOwnString(parsed, 'version')) {
65
109
  return null;
66
110
  }
@@ -68,6 +112,7 @@ export function readPackageJson(path) {
68
112
  const tags = getNxTags(parsed);
69
113
  return {
70
114
  name: parsed.name,
115
+ projectName: packageNxProjectName(parsed),
71
116
  version: parsed.version,
72
117
  private: privateValue,
73
118
  tags,
@@ -76,6 +121,14 @@ export function readPackageJson(path) {
76
121
  json: parsed,
77
122
  };
78
123
  }
124
+ function packageNxProjectName(pkg) {
125
+ const nx = isRecord(pkg.nx) ? pkg.nx : null;
126
+ return (nx ? stringProperty(nx, 'name') : null) ?? stringProperty(pkg, 'name') ?? '';
127
+ }
128
+ export function readPackageJsonObject(path) {
129
+ const parsed = readJsonObject(path);
130
+ return parsed;
131
+ }
79
132
  export function getNxTags(pkg) {
80
133
  if (!hasOwn(pkg, 'nx') || !isRecord(pkg.nx) || !hasOwn(pkg.nx, 'tags') || !Array.isArray(pkg.nx.tags)) {
81
134
  return [];
@@ -96,6 +149,33 @@ export function repositoryInfo(pkg) {
96
149
  }
97
150
  return { type: stringProperty(repository, 'type') ?? 'git', url };
98
151
  }
152
+ export function sameRepositoryAfterNormalization(left, right) {
153
+ return normalizedRepositoryUrl(left) === normalizedRepositoryUrl(right);
154
+ }
155
+ function normalizedRepositoryUrl(url) {
156
+ const trimmed = url
157
+ .trim()
158
+ .replace(/^git\+/, '')
159
+ .replace(/#.*$/, '')
160
+ .replace(/\/$/, '')
161
+ .replace(/\.git$/, '');
162
+ const https = /^https:\/\/github\.com\/([^/]+)\/([^/]+)$/i.exec(trimmed);
163
+ if (https?.[1] && https[2]) {
164
+ return githubRepositoryKey(https[1], https[2]);
165
+ }
166
+ const ssh = /^git@github\.com:([^/]+)\/([^/]+)$/i.exec(trimmed);
167
+ if (ssh?.[1] && ssh[2]) {
168
+ return githubRepositoryKey(ssh[1], ssh[2]);
169
+ }
170
+ const shorthand = /^github:([^/]+)\/([^/]+)$/i.exec(trimmed);
171
+ if (shorthand?.[1] && shorthand[2]) {
172
+ return githubRepositoryKey(shorthand[1], shorthand[2]);
173
+ }
174
+ return trimmed;
175
+ }
176
+ function githubRepositoryKey(owner, repo) {
177
+ return `github:${owner.toLowerCase()}/${repo.toLowerCase()}`;
178
+ }
99
179
  export function escapeRegex(value) {
100
180
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
101
181
  }
@@ -1,2 +1,9 @@
1
- export declare function validateCommitMessage(message: string): string | null;
1
+ export interface FormatCommitMessageOptions {
2
+ wrapBody?: (paragraph: string) => string;
3
+ }
4
+ export interface ValidateCommitMessageOptions {
5
+ validScopes?: ReadonlySet<string>;
6
+ }
7
+ export declare function validateCommitMessage(message: string, options?: ValidateCommitMessageOptions): string | null;
8
+ export declare function formatCommitMessage(message: string, options?: FormatCommitMessageOptions): string;
2
9
  //# sourceMappingURL=commit-msg.d.ts.map