Skip to content

Commit 0868ac7

Browse files
committed
feat: run command improvements
1 parent b6c68bd commit 0868ac7

File tree

13 files changed

+259
-51
lines changed

13 files changed

+259
-51
lines changed

src/app/app.module.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { Module } from '@nestjs/common'
2-
import { InitCommand } from '@/commands/init.command'
2+
import { RunCommand } from '@/commands/run.command'
33
import { ShowCommand } from '@/commands/show.command'
44
import { PackageManagerModule } from '@/pkg-manager'
55

66
@Module({
77
imports: [PackageManagerModule],
8-
providers: [InitCommand, ShowCommand]
8+
providers: [RunCommand, ShowCommand]
99
})
1010
export class AppModule {}

src/commands/init.command.ts

Lines changed: 0 additions & 24 deletions
This file was deleted.

src/commands/run.command.ts

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import { ensureFile } from '@neodx/fs'
2+
import { hasOwn, isNil } from '@neodx/std'
3+
import chalk from 'chalk'
4+
import { Command, CommandRunner, Option } from 'nest-commander'
5+
import { dirname, resolve } from 'node:path'
6+
import type { AbstractPackageManager } from '@/pkg-manager'
7+
import { InjectPackageManager } from '@/pkg-manager'
8+
import { PackageManager } from '@/pkg-manager/pkg-manager.consts'
9+
import type { PackageJson } from '@/shared/json'
10+
import { readJson } from '@/shared/json'
11+
import { addLibraryPrefix } from '@/shared/misc'
12+
13+
export interface BaseInitOptions {
14+
args?: string
15+
}
16+
17+
@Command({
18+
name: 'run',
19+
options: {
20+
isDefault: true
21+
},
22+
description: ''
23+
})
24+
export class RunCommand extends CommandRunner {
25+
constructor(
26+
@InjectPackageManager() private readonly manager: AbstractPackageManager
27+
) {
28+
super()
29+
}
30+
31+
public async run(params: string[], options: BaseInitOptions) {
32+
const [target, project = null] = params
33+
34+
if (!target) {
35+
console.log('no target')
36+
return
37+
}
38+
39+
const startTime = Date.now()
40+
41+
let packageJsonPath = resolve(process.cwd(), 'package.json')
42+
43+
if (project) {
44+
const workspaces = await this.manager.getWorkspaces()
45+
46+
const projectMeta = workspaces.find(
47+
(workspace) => workspace.name === project
48+
)
49+
50+
if (!projectMeta) {
51+
// TODO improve errors rewrite to class
52+
throw new Error(
53+
`Project ${chalk.cyan(project)} not found. Please ensure it exists.`
54+
)
55+
}
56+
57+
packageJsonPath = resolve(projectMeta.location, 'package.json')
58+
}
59+
60+
await ensureFile(packageJsonPath)
61+
62+
const pkg = (await readJson(packageJsonPath)) as PackageJson
63+
64+
const projectName = project ?? pkg.name ?? 'root'
65+
66+
if (isNil(pkg.scripts) || !hasOwn(pkg.scripts, target)) {
67+
// TODO improve errors
68+
throw new Error(
69+
`Could not find target ${chalk.cyan(target)} in project ${chalk.white(projectName)}.`
70+
)
71+
}
72+
73+
const command = this.manager.createRunCommand({
74+
target,
75+
project,
76+
packageJsonPath,
77+
args: options.args
78+
})
79+
80+
try {
81+
// https://github.com/oven-sh/bun/issues/6386
82+
const cwd =
83+
this.manager.agent === PackageManager.BUN
84+
? dirname(packageJsonPath)
85+
: process.cwd()
86+
87+
await this.manager.exec(command, { stdio: 'inherit', cwd })
88+
} catch (error) {
89+
console.log(error)
90+
}
91+
92+
// TODO: improve logger
93+
console.info(
94+
addLibraryPrefix(
95+
`Successfully ran target ${chalk.cyan(target)} for project ${chalk.white(project ?? pkg.name)} (${Date.now() - startTime} ms)`
96+
)
97+
)
98+
}
99+
100+
@Option({
101+
flags: '-args, --args [string]',
102+
name: 'args'
103+
})
104+
public parseArgs(args: string) {
105+
if (!args) return
106+
107+
const isValid = args.match(/^--\w+=\w+$/)
108+
109+
if (!isValid) {
110+
throw new Error('not valid')
111+
}
112+
113+
return args
114+
}
115+
}

src/commands/show.command.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
import { serializeJson } from '@neodx/fs'
12
import chalk from 'chalk'
2-
import { Command, CommandRunner, Option } from 'nest-commander'
3+
import {
4+
CliUtilityService,
5+
Command,
6+
CommandRunner,
7+
Option
8+
} from 'nest-commander'
39
import type { AbstractPackageManager } from '@/pkg-manager'
410
import { InjectPackageManager } from '@/pkg-manager'
511
import { addLibraryPrefix } from '@/shared/misc'
@@ -15,7 +21,8 @@ export interface ShowCommandOptions {
1521
})
1622
export class ShowCommand extends CommandRunner {
1723
constructor(
18-
@InjectPackageManager() private readonly manager: AbstractPackageManager
24+
@InjectPackageManager() private readonly manager: AbstractPackageManager,
25+
private readonly utilityService: CliUtilityService
1926
) {
2027
super()
2128
}
@@ -25,7 +32,8 @@ export class ShowCommand extends CommandRunner {
2532
const workspaces = await this.manager.getWorkspaces()
2633

2734
if (options.json) {
28-
console.info(workspaces)
35+
const serializedJson = serializeJson(workspaces)
36+
console.info(serializedJson)
2937
return
3038
}
3139

@@ -39,9 +47,10 @@ export class ShowCommand extends CommandRunner {
3947
}
4048

4149
@Option({
50+
name: 'json',
4251
flags: '-json, --json [boolean]'
4352
})
44-
public returnAsJson(val: string): boolean {
45-
return JSON.parse(val)
53+
public parseJson(val: string): boolean {
54+
return this.utilityService.parseBoolean(val)
4655
}
4756
}
Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,31 @@
1+
import { isTypeOfString } from '@neodx/std'
2+
import type { Options as ExecaOptions } from 'execa'
13
import { execaCommand as $ } from 'execa'
2-
import type { WorkspaceProject } from '@/pkg-manager/pkg-manager.types'
4+
import type {
5+
RunCommandOptions,
6+
WorkspaceProject
7+
} from '@/pkg-manager/pkg-manager.types'
38

49
export abstract class AbstractPackageManager {
510
constructor(private command: string) {}
611

7-
public abstract getWorkspaces(): Promise<WorkspaceProject[]>
8-
9-
public async exec(...args: string[]) {
10-
const output = await $(`${this.command} ${args.join(' ')}`, {
11-
cwd: process.cwd()
12-
})
12+
public async exec(args: string | string[], options?: ExecaOptions) {
13+
const output = await $(
14+
`${this.command} ${isTypeOfString(args) ? args : args.join(' ')}`,
15+
{
16+
cwd: process.cwd(),
17+
...options
18+
}
19+
)
1320

1421
return output.stdout as string
1522
}
1623

1724
public get agent() {
1825
return this.command
1926
}
27+
28+
public abstract getWorkspaces(): Promise<WorkspaceProject[]>
29+
30+
public abstract createRunCommand(opts: RunCommandOptions): string[]
2031
}

src/pkg-manager/managers/bun.pkg-manager.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import { scan } from '@neodx/fs'
2-
import { resolve } from 'node:path'
2+
import { isTypeOfString } from '@neodx/std'
3+
import { dirname, resolve } from 'node:path'
34
import * as process from 'process'
45
import { AbstractPackageManager } from '@/pkg-manager/managers/abstract.pkg-manager'
56
import { PackageManager } from '@/pkg-manager/pkg-manager.consts'
6-
import type { WorkspaceProject } from '@/pkg-manager/pkg-manager.types'
7+
import type {
8+
RunCommandOptions,
9+
WorkspaceProject
10+
} from '@/pkg-manager/pkg-manager.types'
711
import type { PackageJson } from '@/shared/json'
812
import { readJson } from '@/shared/json'
913

@@ -36,7 +40,7 @@ export class BunPackageManager extends AbstractPackageManager {
3640
const scopedPkgJson = (await readJson(pattern)) as PackageJson
3741

3842
const workspaceName = scopedPkgJson.name ?? null
39-
const workspaceDir = resolve(pattern, '..')
43+
const workspaceDir = dirname(pattern)
4044

4145
return {
4246
name: workspaceName,
@@ -47,4 +51,21 @@ export class BunPackageManager extends AbstractPackageManager {
4751

4852
return bunWorkspaces
4953
}
54+
55+
public createRunCommand(opts: RunCommandOptions): string[] {
56+
const command = ['--silent', 'run', opts.target]
57+
const projectCwd = dirname(opts.packageJsonPath)
58+
59+
if (opts.project) {
60+
// Doesn't work for some reason
61+
// https://github.com/oven-sh/bun/issues/6386
62+
command.push(`--cwd=${projectCwd}`)
63+
}
64+
65+
if (isTypeOfString(opts.args)) {
66+
command.push('--', opts.args)
67+
}
68+
69+
return command
70+
}
5071
}

src/pkg-manager/managers/npm.pkg-manager.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import { parseJson } from '@neodx/fs'
2-
import { entries, hasOwn, isObject } from '@neodx/std'
2+
import { entries, hasOwn, isObject, isTypeOfString } from '@neodx/std'
33
import { resolve } from 'node:path'
44
import { AbstractPackageManager } from '@/pkg-manager/managers/abstract.pkg-manager'
55
import { PackageManager } from '@/pkg-manager/pkg-manager.consts'
6-
import type { WorkspaceProject } from '@/pkg-manager/pkg-manager.types'
6+
import type {
7+
RunCommandOptions,
8+
WorkspaceProject
9+
} from '@/pkg-manager/pkg-manager.types'
710

811
interface NpmWorkspaceMetadata {
912
name?: string
@@ -48,4 +51,18 @@ export class NpmPackageManager extends AbstractPackageManager {
4851

4952
return npmWorkspaces
5053
}
54+
55+
public createRunCommand(opts: RunCommandOptions): string[] {
56+
const command = ['run', opts.target, '--silent']
57+
58+
if (opts.project) {
59+
command.push(`--workspace=${opts.project}`)
60+
}
61+
62+
if (isTypeOfString(opts.args)) {
63+
command.push('--', opts.args)
64+
}
65+
66+
return command
67+
}
5168
}

src/pkg-manager/managers/pnpm.pkg-manager.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import { parseJson } from '@neodx/fs'
2-
import { hasOwn, isObjectLike } from '@neodx/std'
2+
import { hasOwn, isObjectLike, isTypeOfString } from '@neodx/std'
33
import { AbstractPackageManager } from '@/pkg-manager/managers/abstract.pkg-manager'
44
import { PackageManager } from '@/pkg-manager/pkg-manager.consts'
5-
import type { WorkspaceProject } from '@/pkg-manager/pkg-manager.types'
5+
import type {
6+
RunCommandOptions,
7+
WorkspaceProject
8+
} from '@/pkg-manager/pkg-manager.types'
69

710
type PnpmWorkspaceMeta = Array<{
811
name: string
@@ -31,4 +34,20 @@ export class PnpmPackageManager extends AbstractPackageManager {
3134
location: path
3235
}))
3336
}
37+
38+
public createRunCommand(opts: RunCommandOptions): string[] {
39+
const command = ['run', '--silent', opts.target]
40+
41+
if (opts.project) {
42+
command.splice(1, 0, `--filter=${opts.project}`)
43+
}
44+
45+
if (isTypeOfString(opts.args)) {
46+
command.push('--', opts.args)
47+
}
48+
49+
console.log(command)
50+
51+
return command
52+
}
3453
}

src/pkg-manager/managers/yarn-berry.pkg-manager.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import { parseJson } from '@neodx/fs'
2-
import { hasOwn, isObject, isTruthy } from '@neodx/std'
2+
import { hasOwn, isObject, isTruthy, isTypeOfString } from '@neodx/std'
33
import { AbstractPackageManager } from '@/pkg-manager/managers/abstract.pkg-manager'
44
import { PackageManager } from '@/pkg-manager/pkg-manager.consts'
5-
import type { WorkspaceProject } from '@/pkg-manager/pkg-manager.types'
5+
import type {
6+
RunCommandOptions,
7+
WorkspaceProject
8+
} from '@/pkg-manager/pkg-manager.types'
69

710
export class YarnBerryPackageManager extends AbstractPackageManager {
811
constructor() {
@@ -26,4 +29,18 @@ export class YarnBerryPackageManager extends AbstractPackageManager {
2629

2730
return workspaces.filter(isTruthy)
2831
}
32+
33+
public createRunCommand(opts: RunCommandOptions): string[] {
34+
const command = ['--silent', 'run', opts.target]
35+
36+
if (opts.project) {
37+
command.unshift('workspace', opts.project)
38+
}
39+
40+
if (isTypeOfString(opts.args)) {
41+
command.push(opts.args)
42+
}
43+
44+
return command
45+
}
2946
}

0 commit comments

Comments
 (0)