@smoothbricks/cli 0.4.0 → 0.4.2

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/dist/lib/run.d.ts CHANGED
@@ -6,6 +6,7 @@ export declare function runResult(command: string, args: string[], cwd: string,
6
6
  stdout: string;
7
7
  stderr: string;
8
8
  }>;
9
+ export declare function printCommandOutput(stdout: string, stderr: string): void;
9
10
  export declare function findRepoRoot(): Promise<string>;
10
11
  export declare function decode(bytes: Uint8Array): string;
11
12
  //# sourceMappingURL=run.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/lib/run.ts"],"names":[],"mappings":"AAMA,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,oBAAoB,CACxC,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,MAAM,CAAC,CAiBjB;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"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/lib/run.ts"],"names":[],"mappings":"AAMA,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,oBAAoB,CACxC,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,MAAM,CAAC,CAiBjB;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;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAOvE;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
@@ -49,6 +49,14 @@ export async function runResult(command, args, cwd, env) {
49
49
  stderr: decode(result.stderr),
50
50
  };
51
51
  }
52
+ export function printCommandOutput(stdout, stderr) {
53
+ if (stdout.length > 0) {
54
+ console.log(trimTrailingNewline(stdout));
55
+ }
56
+ if (stderr.length > 0) {
57
+ console.error(trimTrailingNewline(stderr));
58
+ }
59
+ }
52
60
  function mergeEnv(env) {
53
61
  const merged = {};
54
62
  for (const [key, value] of Object.entries(process.env)) {
@@ -94,3 +102,6 @@ export async function findRepoRoot() {
94
102
  export function decode(bytes) {
95
103
  return new TextDecoder().decode(bytes);
96
104
  }
105
+ function trimTrailingNewline(value) {
106
+ return value.endsWith('\n') ? value.slice(0, -1) : value;
107
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"managed-files.d.ts","sourceRoot":"","sources":["../../src/monorepo/managed-files.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,iBAAiB,GAAG,SAAS,GAAG,YAAY,CAAC;CACxG;AAwFD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,EAAE,CAG/F;AA4KD,wBAAgB,YAAY,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,IAAI,CAIxD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQzD"}
1
+ {"version":3,"file":"managed-files.d.ts","sourceRoot":"","sources":["../../src/monorepo/managed-files.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,iBAAiB,GAAG,SAAS,GAAG,YAAY,CAAC;CACxG;AAmGD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,EAAE,CAG/F;AA4KD,wBAAgB,YAAY,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,IAAI,CAIxD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQzD"}
@@ -6,6 +6,11 @@ import { listReleasePackages, readPackageJson } from '../lib/workspace.js';
6
6
  import { renderCiWorkflowYaml } from './ci-workflow.js';
7
7
  import { renderPublishWorkflowYaml } from './publish-workflow.js';
8
8
  const managedFiles = [
9
+ {
10
+ kind: 'raw',
11
+ source: 'envrc',
12
+ target: '.envrc',
13
+ },
9
14
  {
10
15
  kind: 'raw',
11
16
  source: 'tooling/direnv/repo-path',
@@ -18,6 +23,12 @@ const managedFiles = [
18
23
  target: 'tooling/direnv/github-actions-bootstrap.sh',
19
24
  executable: true,
20
25
  },
26
+ {
27
+ kind: 'raw',
28
+ source: 'tooling/direnv/setup-environment.ts',
29
+ target: 'tooling/direnv/setup-environment.ts',
30
+ executable: true,
31
+ },
21
32
  {
22
33
  kind: 'raw',
23
34
  source: 'tooling/git-hooks/pre-commit.sh',
@@ -1 +1 @@
1
- {"version":3,"file":"nx-sync.d.ts","sourceRoot":"","sources":["../../src/monorepo/nx-sync.ts"],"names":[],"mappings":"AAEA,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAK5E;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAGnF"}
1
+ {"version":3,"file":"nx-sync.d.ts","sourceRoot":"","sources":["../../src/monorepo/nx-sync.ts"],"names":[],"mappings":"AAEA,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAW5E;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAWnF"}
@@ -1,11 +1,25 @@
1
- import { runStatus } from '../lib/run.js';
1
+ import { printCommandOutput, runResult, runStatus } from '../lib/run.js';
2
2
  export async function fixNxSync(root, verbose = false) {
3
- const status = await runStatus('nx', ['sync'], root, !verbose);
3
+ const result = verbose
4
+ ? { exitCode: await runStatus('nx', ['sync'], root, false), stdout: '', stderr: '' }
5
+ : await runResult('nx', ['sync'], root);
6
+ const status = result.exitCode;
4
7
  if (status !== 0) {
8
+ if (!verbose) {
9
+ printCommandOutput(result.stdout, result.stderr);
10
+ }
5
11
  throw new Error(`nx sync failed with exit code ${status}`);
6
12
  }
7
13
  }
8
14
  export async function validateNxSync(root, verbose = false) {
9
- const status = await runStatus('nx', ['sync:check'], root, !verbose);
10
- return status === 0 ? 0 : 1;
15
+ const result = verbose
16
+ ? { exitCode: await runStatus('nx', ['sync:check'], root, false), stdout: '', stderr: '' }
17
+ : await runResult('nx', ['sync:check'], root);
18
+ if (result.exitCode === 0) {
19
+ return 0;
20
+ }
21
+ if (!verbose) {
22
+ printCommandOutput(result.stdout, result.stderr);
23
+ }
24
+ return 1;
11
25
  }
@@ -1,5 +1,10 @@
1
1
  export interface PackageHygieneShell {
2
2
  run(command: string, args: string[], cwd: string): Promise<void>;
3
+ runResult?(command: string, args: string[], cwd: string): Promise<{
4
+ exitCode: number;
5
+ stdout: string;
6
+ stderr: string;
7
+ }>;
3
8
  runStatus(command: string, args: string[], cwd: string, quiet?: boolean): Promise<number>;
4
9
  }
5
10
  export declare function fixPackageHygiene(root: string, verboseOrShell?: boolean | PackageHygieneShell, maybeShell?: PackageHygieneShell): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"package-hygiene.d.ts","sourceRoot":"","sources":["../../src/monorepo/package-hygiene.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,mBAAmB;IAClC,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3F;AAID,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,cAAc,GAAE,OAAO,GAAG,mBAA2B,EACrD,UAAU,GAAE,mBAAkC,GAC7C,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,MAAM,EACZ,cAAc,GAAE,OAAO,GAAG,mBAA2B,EACrD,UAAU,GAAE,mBAAkC,GAC7C,OAAO,CAAC,MAAM,CAAC,CASjB"}
1
+ {"version":3,"file":"package-hygiene.d.ts","sourceRoot":"","sources":["../../src/monorepo/package-hygiene.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,mBAAmB;IAClC,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,SAAS,CAAC,CACR,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,MAAM,GACV,OAAO,CAAC;QACT,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;IACH,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3F;AAID,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,cAAc,GAAE,OAAO,GAAG,mBAA2B,EACrD,UAAU,GAAE,mBAAkC,GAC7C,OAAO,CAAC,IAAI,CAAC,CAWf;AAED,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,MAAM,EACZ,cAAc,GAAE,OAAO,GAAG,mBAA2B,EACrD,UAAU,GAAE,mBAAkC,GAC7C,OAAO,CAAC,MAAM,CAAC,CAajB"}
@@ -1,20 +1,34 @@
1
- import { run, runStatus } from '../lib/run.js';
2
- const defaultShell = { run, runStatus };
1
+ import { printCommandOutput, run, runResult, runStatus } from '../lib/run.js';
2
+ const defaultShell = { run, runResult, runStatus };
3
3
  export async function fixPackageHygiene(root, verboseOrShell = false, maybeShell = defaultShell) {
4
4
  const verbose = typeof verboseOrShell === 'boolean' ? verboseOrShell : false;
5
5
  const shell = typeof verboseOrShell === 'boolean' ? maybeShell : verboseOrShell;
6
- const status = await shell.runStatus('sherif', ['-f', '--select', 'highest'], root, !verbose);
6
+ const result = await runPackageHygieneCommand(shell, 'sherif', ['-f', '--select', 'highest'], root, verbose);
7
+ const status = result.exitCode;
7
8
  if (status !== 0) {
9
+ if (!verbose) {
10
+ printCommandOutput(result.stdout, result.stderr);
11
+ }
8
12
  throw new Error(`sherif -f --select highest failed with exit code ${status}`);
9
13
  }
10
14
  }
11
15
  export async function validatePackageHygiene(root, verboseOrShell = false, maybeShell = defaultShell) {
12
16
  const verbose = typeof verboseOrShell === 'boolean' ? verboseOrShell : false;
13
17
  const shell = typeof verboseOrShell === 'boolean' ? maybeShell : verboseOrShell;
14
- const status = await shell.runStatus('sherif', ['--fail-on-warnings'], root, !verbose);
18
+ const result = await runPackageHygieneCommand(shell, 'sherif', ['--fail-on-warnings'], root, verbose);
19
+ const status = result.exitCode;
15
20
  if (status !== 0) {
21
+ if (!verbose) {
22
+ printCommandOutput(result.stdout, result.stderr);
23
+ }
16
24
  console.error('sherif package hygiene validation failed');
17
25
  return 1;
18
26
  }
19
27
  return 0;
20
28
  }
29
+ async function runPackageHygieneCommand(shell, command, args, root, verbose) {
30
+ if (!verbose && shell.runResult) {
31
+ return shell.runResult(command, args, root);
32
+ }
33
+ return { exitCode: await shell.runStatus(command, args, root, !verbose), stdout: '', stderr: '' };
34
+ }
@@ -3,7 +3,7 @@ import { join } from 'node:path';
3
3
  import { publint } from 'publint';
4
4
  import { formatMessage } from 'publint/utils';
5
5
  import { isRecord } from '../lib/json.js';
6
- import { runResult, runStatus } from '../lib/run.js';
6
+ import { printCommandOutput, runResult } from '../lib/run.js';
7
7
  import { listPublicPackages } from '../lib/workspace.js';
8
8
  import { readPackedPackageJson, validatePackedWorkspaceDependencies } from './packed-manifest.js';
9
9
  export async function validatePackedPublicPackages(root) {
@@ -116,9 +116,10 @@ async function packPackage(root, pkg) {
116
116
  const tarballName = `.smoo-${process.pid}-${Date.now()}.tgz`;
117
117
  const tarballPath = join(root, tarballName);
118
118
  try {
119
- const status = await runStatus('bun', ['pm', 'pack', '--filename', tarballName, '--ignore-scripts', '--quiet'], packageDir, true);
120
- if (status !== 0) {
121
- throw new Error(`bun pm pack failed with exit code ${status}`);
119
+ const result = await runResult('bun', ['pm', 'pack', '--filename', tarballName, '--ignore-scripts', '--quiet'], packageDir);
120
+ if (result.exitCode !== 0) {
121
+ printCommandOutput(result.stdout, result.stderr);
122
+ throw new Error(`bun pm pack failed with exit code ${result.exitCode}`);
122
123
  }
123
124
  const bytes = new Uint8Array(readFileSync(tarballPath));
124
125
  return { path: tarballPath, arrayBuffer: bytes.slice().buffer };
@@ -1,6 +1,6 @@
1
1
  import { chmodSync, existsSync, statSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
- import { runStatus } from '../../lib/run.js';
3
+ import { printCommandOutput, runResult, runStatus } from '../../lib/run.js';
4
4
  import { readProjectTargets } from '../../nx/index.js';
5
5
  import { syncBunLockfileVersions, validateBunLockfileVersions } from '../lockfile.js';
6
6
  import { validateManagedFiles } from '../managed-files.js';
@@ -221,10 +221,14 @@ async function runBuild(ctx, options = {}) {
221
221
  if (options.verbose) {
222
222
  printCheckHeading('build', true);
223
223
  }
224
- const status = await runStatus('nx', ['run-many', '-t', 'build'], ctx.root, options.verbose !== true);
224
+ const result = options.verbose
225
+ ? { exitCode: await runStatus('nx', ['run-many', '-t', 'build'], ctx.root, false), stdout: '', stderr: '' }
226
+ : await runResult('nx', ['run-many', '-t', 'build'], ctx.root);
227
+ const status = result.exitCode;
225
228
  if (status !== 0) {
226
229
  if (!options.verbose) {
227
230
  printCheckHeading('build', true);
231
+ printCommandOutput(result.stdout, result.stderr);
228
232
  }
229
233
  console.error('nx run-many -t build failed');
230
234
  printCheckStatus('build', 1);
@@ -292,6 +296,8 @@ async function readResolvedTargetsByProject(ctx) {
292
296
  {
293
297
  targets: new Set(project.targets),
294
298
  ...(project.buildDependsOn ? { buildDependsOn: project.buildDependsOn } : {}),
299
+ ...(project.targetExecutors ? { targetExecutors: project.targetExecutors } : {}),
300
+ ...(project.targetScripts ? { targetScripts: project.targetScripts } : {}),
295
301
  },
296
302
  ]));
297
303
  }
@@ -2,6 +2,8 @@ export interface ProjectTargets {
2
2
  project: string;
3
3
  targets: string[];
4
4
  buildDependsOn?: string[];
5
+ targetExecutors?: Map<string, string>;
6
+ targetScripts?: Map<string, string>;
5
7
  }
6
8
  export interface CommandInvocation {
7
9
  command: string;
@@ -12,6 +14,8 @@ export declare function nxShowProjectCommand(project: string): CommandInvocation
12
14
  export declare function nxCacheDirectories(root: string): string[];
13
15
  export declare function targetNamesFromNxProjectJson(value: unknown): string[];
14
16
  export declare function buildDependsOnFromNxProjectJson(value: unknown): string[] | undefined;
17
+ export declare function targetExecutorsFromNxProjectJson(value: unknown): Map<string, string>;
18
+ export declare function targetScriptsFromNxProjectJson(value: unknown): Map<string, string>;
15
19
  export declare function formatProjectTargetLines(projects: ProjectTargets[]): string;
16
20
  export declare function projectNamesWithTarget(projects: ProjectTargets[], target: string): string[];
17
21
  export declare function projectNamesFromNxShowProjectsOutput(output: string): string[];
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/nx/index.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,wBAAgB,cAAc,IAAI,iBAAiB,CAElD;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,CAEvE;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAEzD;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAGrE;AAED,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,GAAG,SAAS,CAOpF;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,MAAM,CAK3E;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,cAAc,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAK3F;AAED,wBAAgB,oCAAoC,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAsB7E;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAK7D;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAQhG;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG5D;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAa5D;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAGhF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/nx/index.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,wBAAgB,cAAc,IAAI,iBAAiB,CAElD;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,CAEvE;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAEzD;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAGrE;AAED,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,GAAG,SAAS,CAOpF;AAED,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAYpF;AAED,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAgBlF;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,MAAM,CAK3E;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,cAAc,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAK3F;AAED,wBAAgB,oCAAoC,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAsB7E;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAK7D;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAQhG;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG5D;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAa5D;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAGhF"}
package/dist/nx/index.js CHANGED
@@ -25,6 +25,36 @@ export function buildDependsOnFromNxProjectJson(value) {
25
25
  }
26
26
  return build.dependsOn;
27
27
  }
28
+ export function targetExecutorsFromNxProjectJson(value) {
29
+ const targets = isRecord(value) ? recordProperty(value, 'targets') : null;
30
+ const executors = new Map();
31
+ if (!targets) {
32
+ return executors;
33
+ }
34
+ for (const [targetName, target] of Object.entries(targets)) {
35
+ if (isRecord(target) && typeof target.executor === 'string') {
36
+ executors.set(targetName, target.executor);
37
+ }
38
+ }
39
+ return executors;
40
+ }
41
+ export function targetScriptsFromNxProjectJson(value) {
42
+ const targets = isRecord(value) ? recordProperty(value, 'targets') : null;
43
+ const scripts = new Map();
44
+ if (!targets) {
45
+ return scripts;
46
+ }
47
+ for (const [targetName, target] of Object.entries(targets)) {
48
+ if (!isRecord(target)) {
49
+ continue;
50
+ }
51
+ const options = recordProperty(target, 'options');
52
+ if (typeof options?.script === 'string') {
53
+ scripts.set(targetName, options.script);
54
+ }
55
+ }
56
+ return scripts;
57
+ }
28
58
  export function formatProjectTargetLines(projects) {
29
59
  return projects
30
60
  .flatMap((project) => project.targets.map((target) => `${project.project}:${target}`))
@@ -108,5 +138,7 @@ async function readProjectTarget(root, project) {
108
138
  project,
109
139
  targets: targetNamesFromNxProjectJson(parsed),
110
140
  buildDependsOn: buildDependsOnFromNxProjectJson(parsed),
141
+ targetExecutors: targetExecutorsFromNxProjectJson(parsed),
142
+ targetScripts: targetScriptsFromNxProjectJson(parsed),
111
143
  };
112
144
  }
@@ -0,0 +1,18 @@
1
+ # Worktrees inherit the real project .envrc once, then stop. The same file is
2
+ # checked out inside .jcode-worktrees, so continuing after source_up_if_exists
3
+ # would run the devenv setup twice while sandbox blocks access to tooling/.
4
+ case "$PWD" in
5
+ */.jcode-worktrees/*)
6
+ ;;
7
+ *)
8
+ source_up_if_exists
9
+ ;;
10
+ esac
11
+
12
+ watch_file tooling/direnv/envrc.sh
13
+
14
+ cd tooling/direnv
15
+ . envrc.sh
16
+
17
+ # Load local extensions and overrides
18
+ source_env_if_exists .envrc-local
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env bun
2
+ import { existsSync } from 'node:fs';
3
+ import { mkdir, rmdir } from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import { $ } from 'bun';
6
+
7
+ const devenvRoot = process.env.DEVENV_ROOT;
8
+ const projectRoot = path.resolve(`${devenvRoot}/../..`);
9
+ const startedWithoutNodeModules = !existsSync(path.join(projectRoot, 'node_modules'));
10
+
11
+ class CapturedCommandError extends Error {
12
+ constructor(
13
+ public readonly command: string,
14
+ public readonly exitCode: number,
15
+ public readonly stdout: Uint8Array,
16
+ public readonly stderr: Uint8Array,
17
+ ) {
18
+ super(`${command} failed with exit code ${exitCode}`);
19
+ }
20
+ }
21
+
22
+ // Go to project root
23
+ process.chdir(projectRoot);
24
+
25
+ try {
26
+ // Install dependencies first so node_modules/.bin tools are available
27
+ if (process.env.CI) {
28
+ try {
29
+ await runSetupCommand('bun install --frozen-lockfile', $`bun install --frozen-lockfile`, { quiet: false });
30
+ } catch (error) {
31
+ console.error('! Failed to install dependencies with frozen lockfile');
32
+ replayCapturedOutput(error);
33
+ await runSetupCommand('bun install', $`bun install`, { quiet: false });
34
+ console.error('git diff after install:');
35
+ await runSetupCommand('git diff', $`git diff`, { quiet: false, allowNonzero: true });
36
+ process.exit(1);
37
+ }
38
+ } else {
39
+ await installLocalDependencies();
40
+ }
41
+
42
+ // Bun selects its resolver mode at process startup. If this process started
43
+ // before node_modules existed, imports below can stay in auto-install mode
44
+ // even after bun install succeeds, so restart once into the real workspace.
45
+ if (startedWithoutNodeModules) {
46
+ await runSetupCommand('restart setup-environment.ts', $`bun --no-install ${import.meta.path}`, { quiet: false });
47
+ process.exit(0);
48
+ }
49
+
50
+ if (!process.env.CI) {
51
+ const { syncRootRuntimeVersions } = await import('@smoothbricks/cli/monorepo/runtime');
52
+ await syncRootRuntimeVersions(projectRoot);
53
+ }
54
+
55
+ const { applyWorkspaceGitConfig } = await import('@smoothbricks/cli/monorepo/git-config');
56
+ await applyWorkspaceGitConfig(projectRoot);
57
+ } catch (error) {
58
+ if (error instanceof CapturedCommandError) {
59
+ console.error(`--- ERROR: setup-environment.ts failed while running: ${error.command}`);
60
+ console.error(`exit code: ${error.exitCode}`);
61
+ } else {
62
+ console.error(`--- ERROR: setup-environment.ts failed: ${error}`);
63
+ }
64
+ replayCapturedOutput(error);
65
+ console.error('\n---');
66
+ process.exit(1);
67
+ }
68
+
69
+ async function installLocalDependencies(): Promise<void> {
70
+ // bun install runs the root prepare script, which patches TypeScript with
71
+ // ts-patch. Multiple concurrent direnv activations can otherwise race while
72
+ // mutating the same files under node_modules.
73
+ await withSetupLock(async () => {
74
+ await runSetupCommand('bun install --no-summary', $`bun install --no-summary`);
75
+ });
76
+ }
77
+
78
+ async function runSetupCommand(
79
+ command: string,
80
+ shell: ReturnType<typeof $>,
81
+ options: { quiet?: boolean; allowNonzero?: boolean } = {},
82
+ ): Promise<void> {
83
+ const result = await shell
84
+ .quiet(options.quiet ?? true)
85
+ .nothrow()
86
+ .cwd(projectRoot);
87
+ if (result.exitCode !== 0 && options.allowNonzero !== true) {
88
+ throw new CapturedCommandError(command, result.exitCode, result.stdout, result.stderr);
89
+ }
90
+ }
91
+
92
+ async function withSetupLock<T>(fn: () => Promise<T>): Promise<T> {
93
+ const lockDir = path.join(projectRoot, 'tooling/direnv/.devenv/setup-environment.lock');
94
+ await acquireLock(lockDir);
95
+ try {
96
+ return await fn();
97
+ } finally {
98
+ await rmdir(lockDir).catch(() => undefined);
99
+ }
100
+ }
101
+
102
+ async function acquireLock(lockDir: string): Promise<void> {
103
+ const deadline = Date.now() + 120_000;
104
+ while (true) {
105
+ try {
106
+ await mkdir(lockDir, { recursive: false });
107
+ return;
108
+ } catch (error) {
109
+ if (!isFileExistsError(error) || Date.now() > deadline) {
110
+ throw error;
111
+ }
112
+ await Bun.sleep(100);
113
+ }
114
+ }
115
+ }
116
+
117
+ function isFileExistsError(error: unknown): boolean {
118
+ return error instanceof Error && 'code' in error && error.code === 'EEXIST';
119
+ }
120
+
121
+ function replayCapturedOutput(error: unknown): void {
122
+ if (!(error instanceof CapturedCommandError)) {
123
+ return;
124
+ }
125
+ if (error.stdout.length > 0) {
126
+ process.stdout.write(error.stdout);
127
+ }
128
+ if (error.stderr.length > 0) {
129
+ process.stderr.write(error.stderr);
130
+ }
131
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smoothbricks/cli",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "type": "module",
5
5
  "description": "SmoothBricks monorepo automation CLI",
6
6
  "bin": {
@@ -45,7 +45,7 @@
45
45
  ],
46
46
  "dependencies": {
47
47
  "@arethetypeswrong/cli": "^0.18.2",
48
- "@smoothbricks/nx-plugin": "0.2.4",
48
+ "@smoothbricks/nx-plugin": "0.2.5",
49
49
  "@smoothbricks/validation": "0.1.1",
50
50
  "commander": "^14.0.3",
51
51
  "publint": "^0.3.18",
package/src/lib/run.ts CHANGED
@@ -70,6 +70,15 @@ export async function runResult(
70
70
  };
71
71
  }
72
72
 
73
+ export function printCommandOutput(stdout: string, stderr: string): void {
74
+ if (stdout.length > 0) {
75
+ console.log(trimTrailingNewline(stdout));
76
+ }
77
+ if (stderr.length > 0) {
78
+ console.error(trimTrailingNewline(stderr));
79
+ }
80
+ }
81
+
73
82
  function mergeEnv(env: Record<string, string>): Record<string, string> {
74
83
  const merged: Record<string, string> = {};
75
84
  for (const [key, value] of Object.entries(process.env)) {
@@ -118,3 +127,7 @@ export async function findRepoRoot(): Promise<string> {
118
127
  export function decode(bytes: Uint8Array): string {
119
128
  return new TextDecoder().decode(bytes);
120
129
  }
130
+
131
+ function trimTrailingNewline(value: string): string {
132
+ return value.endsWith('\n') ? value.slice(0, -1) : value;
133
+ }
@@ -38,6 +38,11 @@ interface DeployTargetInfo {
38
38
  }
39
39
 
40
40
  const managedFiles: ManagedFile[] = [
41
+ {
42
+ kind: 'raw',
43
+ source: 'envrc',
44
+ target: '.envrc',
45
+ },
41
46
  {
42
47
  kind: 'raw',
43
48
  source: 'tooling/direnv/repo-path',
@@ -50,6 +55,12 @@ const managedFiles: ManagedFile[] = [
50
55
  target: 'tooling/direnv/github-actions-bootstrap.sh',
51
56
  executable: true,
52
57
  },
58
+ {
59
+ kind: 'raw',
60
+ source: 'tooling/direnv/setup-environment.ts',
61
+ target: 'tooling/direnv/setup-environment.ts',
62
+ executable: true,
63
+ },
53
64
  {
54
65
  kind: 'raw',
55
66
  source: 'tooling/git-hooks/pre-commit.sh',
@@ -1,13 +1,27 @@
1
- import { runStatus } from '../lib/run.js';
1
+ import { printCommandOutput, runResult, runStatus } from '../lib/run.js';
2
2
 
3
3
  export async function fixNxSync(root: string, verbose = false): Promise<void> {
4
- const status = await runStatus('nx', ['sync'], root, !verbose);
4
+ const result = verbose
5
+ ? { exitCode: await runStatus('nx', ['sync'], root, false), stdout: '', stderr: '' }
6
+ : await runResult('nx', ['sync'], root);
7
+ const status = result.exitCode;
5
8
  if (status !== 0) {
9
+ if (!verbose) {
10
+ printCommandOutput(result.stdout, result.stderr);
11
+ }
6
12
  throw new Error(`nx sync failed with exit code ${status}`);
7
13
  }
8
14
  }
9
15
 
10
16
  export async function validateNxSync(root: string, verbose = false): Promise<number> {
11
- const status = await runStatus('nx', ['sync:check'], root, !verbose);
12
- return status === 0 ? 0 : 1;
17
+ const result = verbose
18
+ ? { exitCode: await runStatus('nx', ['sync:check'], root, false), stdout: '', stderr: '' }
19
+ : await runResult('nx', ['sync:check'], root);
20
+ if (result.exitCode === 0) {
21
+ return 0;
22
+ }
23
+ if (!verbose) {
24
+ printCommandOutput(result.stdout, result.stderr);
25
+ }
26
+ return 1;
13
27
  }
@@ -1,7 +1,12 @@
1
- import { describe, expect, it } from 'bun:test';
1
+ import { afterEach, describe, expect, it, spyOn } from 'bun:test';
2
2
  import { fixPackageHygiene, type PackageHygieneShell, validatePackageHygiene } from './package-hygiene.js';
3
3
 
4
4
  describe('package hygiene', () => {
5
+ afterEach(() => {
6
+ console.log = originalConsoleLog;
7
+ console.error = originalConsoleError;
8
+ });
9
+
5
10
  it('runs sherif in autofix mode and selects highest dependency versions', async () => {
6
11
  const shell = new RecordingShell();
7
12
 
@@ -17,8 +22,39 @@ describe('package hygiene', () => {
17
22
 
18
23
  expect(shell.statuses).toEqual([{ command: 'sherif', args: ['--fail-on-warnings'], cwd: '/repo' }]);
19
24
  });
25
+
26
+ it('prints captured sherif output when quiet validation fails', async () => {
27
+ const shell = new FailingResultShell('sherif stdout', 'sherif stderr');
28
+ const logs = captureConsoleLogs();
29
+ const errors = captureConsoleErrors();
30
+
31
+ const failures = await validatePackageHygiene('/repo', shell);
32
+
33
+ expect(failures).toBe(1);
34
+ expect(logs).toEqual(['sherif stdout']);
35
+ expect(errors).toEqual(['sherif stderr', 'sherif package hygiene validation failed']);
36
+ });
20
37
  });
21
38
 
39
+ const originalConsoleLog = console.log;
40
+ const originalConsoleError = console.error;
41
+
42
+ function captureConsoleLogs(): string[] {
43
+ const logs: string[] = [];
44
+ spyOn(console, 'log').mockImplementation((...args: unknown[]) => {
45
+ logs.push(args.join(' '));
46
+ });
47
+ return logs;
48
+ }
49
+
50
+ function captureConsoleErrors(): string[] {
51
+ const errors: string[] = [];
52
+ spyOn(console, 'error').mockImplementation((...args: unknown[]) => {
53
+ errors.push(args.join(' '));
54
+ });
55
+ return errors;
56
+ }
57
+
22
58
  class RecordingShell implements PackageHygieneShell {
23
59
  readonly runs: { command: string; args: string[]; cwd: string }[] = [];
24
60
  readonly statuses: { command: string; args: string[]; cwd: string }[] = [];
@@ -32,3 +68,20 @@ class RecordingShell implements PackageHygieneShell {
32
68
  return 0;
33
69
  }
34
70
  }
71
+
72
+ class FailingResultShell implements PackageHygieneShell {
73
+ constructor(
74
+ private readonly stdout: string,
75
+ private readonly stderr: string,
76
+ ) {}
77
+
78
+ async run(): Promise<void> {}
79
+
80
+ async runResult(): Promise<{ exitCode: number; stdout: string; stderr: string }> {
81
+ return { exitCode: 1, stdout: this.stdout, stderr: this.stderr };
82
+ }
83
+
84
+ async runStatus(): Promise<number> {
85
+ return 1;
86
+ }
87
+ }
@@ -1,11 +1,20 @@
1
- import { run, runStatus } from '../lib/run.js';
1
+ import { printCommandOutput, run, runResult, runStatus } from '../lib/run.js';
2
2
 
3
3
  export interface PackageHygieneShell {
4
4
  run(command: string, args: string[], cwd: string): Promise<void>;
5
+ runResult?(
6
+ command: string,
7
+ args: string[],
8
+ cwd: string,
9
+ ): Promise<{
10
+ exitCode: number;
11
+ stdout: string;
12
+ stderr: string;
13
+ }>;
5
14
  runStatus(command: string, args: string[], cwd: string, quiet?: boolean): Promise<number>;
6
15
  }
7
16
 
8
- const defaultShell: PackageHygieneShell = { run, runStatus };
17
+ const defaultShell: PackageHygieneShell = { run, runResult, runStatus };
9
18
 
10
19
  export async function fixPackageHygiene(
11
20
  root: string,
@@ -14,8 +23,12 @@ export async function fixPackageHygiene(
14
23
  ): Promise<void> {
15
24
  const verbose = typeof verboseOrShell === 'boolean' ? verboseOrShell : false;
16
25
  const shell = typeof verboseOrShell === 'boolean' ? maybeShell : verboseOrShell;
17
- const status = await shell.runStatus('sherif', ['-f', '--select', 'highest'], root, !verbose);
26
+ const result = await runPackageHygieneCommand(shell, 'sherif', ['-f', '--select', 'highest'], root, verbose);
27
+ const status = result.exitCode;
18
28
  if (status !== 0) {
29
+ if (!verbose) {
30
+ printCommandOutput(result.stdout, result.stderr);
31
+ }
19
32
  throw new Error(`sherif -f --select highest failed with exit code ${status}`);
20
33
  }
21
34
  }
@@ -27,10 +40,27 @@ export async function validatePackageHygiene(
27
40
  ): Promise<number> {
28
41
  const verbose = typeof verboseOrShell === 'boolean' ? verboseOrShell : false;
29
42
  const shell = typeof verboseOrShell === 'boolean' ? maybeShell : verboseOrShell;
30
- const status = await shell.runStatus('sherif', ['--fail-on-warnings'], root, !verbose);
43
+ const result = await runPackageHygieneCommand(shell, 'sherif', ['--fail-on-warnings'], root, verbose);
44
+ const status = result.exitCode;
31
45
  if (status !== 0) {
46
+ if (!verbose) {
47
+ printCommandOutput(result.stdout, result.stderr);
48
+ }
32
49
  console.error('sherif package hygiene validation failed');
33
50
  return 1;
34
51
  }
35
52
  return 0;
36
53
  }
54
+
55
+ async function runPackageHygieneCommand(
56
+ shell: PackageHygieneShell,
57
+ command: string,
58
+ args: string[],
59
+ root: string,
60
+ verbose: boolean,
61
+ ): Promise<{ exitCode: number; stdout: string; stderr: string }> {
62
+ if (!verbose && shell.runResult) {
63
+ return shell.runResult(command, args, root);
64
+ }
65
+ return { exitCode: await shell.runStatus(command, args, root, !verbose), stdout: '', stderr: '' };
66
+ }
@@ -3,7 +3,7 @@ import { join } from 'node:path';
3
3
  import { publint } from 'publint';
4
4
  import { formatMessage } from 'publint/utils';
5
5
  import { isRecord } from '../lib/json.js';
6
- import { runResult, runStatus } from '../lib/run.js';
6
+ import { printCommandOutput, runResult } from '../lib/run.js';
7
7
  import type { PackageInfo } from '../lib/workspace.js';
8
8
  import { listPublicPackages } from '../lib/workspace.js';
9
9
  import { readPackedPackageJson, validatePackedWorkspaceDependencies } from './packed-manifest.js';
@@ -142,14 +142,14 @@ async function packPackage(root: string, pkg: PackageInfo): Promise<{ path: stri
142
142
  const tarballName = `.smoo-${process.pid}-${Date.now()}.tgz`;
143
143
  const tarballPath = join(root, tarballName);
144
144
  try {
145
- const status = await runStatus(
145
+ const result = await runResult(
146
146
  'bun',
147
147
  ['pm', 'pack', '--filename', tarballName, '--ignore-scripts', '--quiet'],
148
148
  packageDir,
149
- true,
150
149
  );
151
- if (status !== 0) {
152
- throw new Error(`bun pm pack failed with exit code ${status}`);
150
+ if (result.exitCode !== 0) {
151
+ printCommandOutput(result.stdout, result.stderr);
152
+ throw new Error(`bun pm pack failed with exit code ${result.exitCode}`);
153
153
  }
154
154
  const bytes = new Uint8Array(readFileSync(tarballPath));
155
155
  return { path: tarballPath, arrayBuffer: bytes.slice().buffer };
@@ -1,6 +1,6 @@
1
1
  import { chmodSync, existsSync, statSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
- import { runStatus } from '../../lib/run.js';
3
+ import { printCommandOutput, runResult, runStatus } from '../../lib/run.js';
4
4
  import { readProjectTargets } from '../../nx/index.js';
5
5
  import { syncBunLockfileVersions, validateBunLockfileVersions } from '../lockfile.js';
6
6
  import { validateManagedFiles } from '../managed-files.js';
@@ -315,10 +315,14 @@ async function runBuild(ctx: MonorepoContext, options: ValidatePackOptions = {})
315
315
  if (options.verbose) {
316
316
  printCheckHeading('build', true);
317
317
  }
318
- const status = await runStatus('nx', ['run-many', '-t', 'build'], ctx.root, options.verbose !== true);
318
+ const result = options.verbose
319
+ ? { exitCode: await runStatus('nx', ['run-many', '-t', 'build'], ctx.root, false), stdout: '', stderr: '' }
320
+ : await runResult('nx', ['run-many', '-t', 'build'], ctx.root);
321
+ const status = result.exitCode;
319
322
  if (status !== 0) {
320
323
  if (!options.verbose) {
321
324
  printCheckHeading('build', true);
325
+ printCommandOutput(result.stdout, result.stderr);
322
326
  }
323
327
  console.error('nx run-many -t build failed');
324
328
  printCheckStatus('build', 1);
@@ -391,6 +395,8 @@ async function readResolvedTargetsByProject(ctx: MonorepoContext): Promise<Map<s
391
395
  {
392
396
  targets: new Set(project.targets),
393
397
  ...(project.buildDependsOn ? { buildDependsOn: project.buildDependsOn } : {}),
398
+ ...(project.targetExecutors ? { targetExecutors: project.targetExecutors } : {}),
399
+ ...(project.targetScripts ? { targetScripts: project.targetScripts } : {}),
394
400
  },
395
401
  ]),
396
402
  );
package/src/nx/index.ts CHANGED
@@ -9,6 +9,8 @@ export interface ProjectTargets {
9
9
  project: string;
10
10
  targets: string[];
11
11
  buildDependsOn?: string[];
12
+ targetExecutors?: Map<string, string>;
13
+ targetScripts?: Map<string, string>;
12
14
  }
13
15
 
14
16
  export interface CommandInvocation {
@@ -42,6 +44,38 @@ export function buildDependsOnFromNxProjectJson(value: unknown): string[] | unde
42
44
  return build.dependsOn;
43
45
  }
44
46
 
47
+ export function targetExecutorsFromNxProjectJson(value: unknown): Map<string, string> {
48
+ const targets = isRecord(value) ? recordProperty(value, 'targets') : null;
49
+ const executors = new Map<string, string>();
50
+ if (!targets) {
51
+ return executors;
52
+ }
53
+ for (const [targetName, target] of Object.entries(targets)) {
54
+ if (isRecord(target) && typeof target.executor === 'string') {
55
+ executors.set(targetName, target.executor);
56
+ }
57
+ }
58
+ return executors;
59
+ }
60
+
61
+ export function targetScriptsFromNxProjectJson(value: unknown): Map<string, string> {
62
+ const targets = isRecord(value) ? recordProperty(value, 'targets') : null;
63
+ const scripts = new Map<string, string>();
64
+ if (!targets) {
65
+ return scripts;
66
+ }
67
+ for (const [targetName, target] of Object.entries(targets)) {
68
+ if (!isRecord(target)) {
69
+ continue;
70
+ }
71
+ const options = recordProperty(target, 'options');
72
+ if (typeof options?.script === 'string') {
73
+ scripts.set(targetName, options.script);
74
+ }
75
+ }
76
+ return scripts;
77
+ }
78
+
45
79
  export function formatProjectTargetLines(projects: ProjectTargets[]): string {
46
80
  return projects
47
81
  .flatMap((project) => project.targets.map((target) => `${project.project}:${target}`))
@@ -135,5 +169,7 @@ async function readProjectTarget(root: string, project: string): Promise<Project
135
169
  project,
136
170
  targets: targetNamesFromNxProjectJson(parsed),
137
171
  buildDependsOn: buildDependsOnFromNxProjectJson(parsed),
172
+ targetExecutors: targetExecutorsFromNxProjectJson(parsed),
173
+ targetScripts: targetScriptsFromNxProjectJson(parsed),
138
174
  };
139
175
  }