Skip to content

Commit fdc29fc

Browse files
committed
feat: migrate command
1 parent b65808e commit fdc29fc

File tree

11 files changed

+152
-18
lines changed

11 files changed

+152
-18
lines changed

src/commands/index.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { RunCommand } from './run/run.command'
2-
import { ShowCommand } from './show/show.command'
1+
import { MigrateCommand } from '@/commands/migrate/migrate.command'
2+
import { RunCommand } from '@/commands/run/run.command'
3+
import { ShowCommand } from '@/commands/show/show.command'
34

4-
export const Commands = [RunCommand, ShowCommand]
5+
export const Commands = [MigrateCommand, RunCommand, ShowCommand]
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { assertDir, ensureFile } from '@neodx/fs'
2+
import { mapValues } from '@neodx/std'
3+
import { Inject } from '@nestjs/common'
4+
import {
5+
CliUtilityService,
6+
Command,
7+
CommandRunner,
8+
Option
9+
} from 'nest-commander'
10+
import { resolve } from 'node:path'
11+
import { ConfigService } from '@/config'
12+
import { LoggerService } from '@/logger'
13+
import type { AbstractPackageManager } from '@/pkg-manager'
14+
import { InjectPackageManager } from '@/pkg-manager'
15+
import { ROOT_PROJECT } from '@/pkg-manager/pkg-manager.consts'
16+
import type { WorkspaceProject } from '@/pkg-manager/pkg-manager.types'
17+
import type { TargetOptions } from '@/resolver/targets/targets-resolver.schema'
18+
import type { PackageJson, PackageScripts } from '@/shared/json'
19+
import { writeJson } from '@/shared/json'
20+
import { invariant } from '@/shared/misc'
21+
22+
export interface MigrateCommandOptions {
23+
all?: boolean
24+
}
25+
26+
@Command({
27+
name: 'migrate',
28+
// TODO: add descriptions
29+
description: ''
30+
})
31+
export class MigrateCommand extends CommandRunner {
32+
constructor(
33+
@InjectPackageManager() private readonly manager: AbstractPackageManager,
34+
@Inject(LoggerService) private readonly logger: LoggerService,
35+
@Inject(ConfigService) private readonly cfg: ConfigService,
36+
private readonly utilityService: CliUtilityService
37+
) {
38+
super()
39+
}
40+
41+
public async run(params: string[], options: MigrateCommandOptions) {
42+
await this.manager.computeWorkspaceProjects()
43+
44+
const { projects: workspaceProjects } = this.manager
45+
46+
if (options.all) {
47+
await Promise.all(
48+
workspaceProjects.map(async (projectMeta) =>
49+
this.migrateProjectCommands(projectMeta, options)
50+
)
51+
)
52+
return
53+
}
54+
55+
const [projectName = ROOT_PROJECT] = params
56+
57+
const projectMeta = workspaceProjects.find(
58+
({ name }) => name === projectName
59+
)
60+
61+
invariant(projectMeta, `Project '${projectName}' not found.`)
62+
63+
await this.migrateProjectCommands(projectMeta)
64+
}
65+
66+
private async migrateProjectCommands(
67+
projectMeta: WorkspaceProject,
68+
options?: MigrateCommandOptions
69+
): Promise<void> {
70+
const { location: projectCwd, type: targetType } = projectMeta
71+
72+
await assertDir(projectMeta.location)
73+
74+
if (targetType === 'package-scripts') {
75+
const commandsFilePath = resolve(
76+
projectMeta.location,
77+
this.cfg.commandsFile
78+
)
79+
const pkgScripts = projectMeta.targets as NonNullable<
80+
PackageJson['scripts']
81+
>
82+
83+
await ensureFile(commandsFilePath)
84+
85+
const serializedTargets = mapValues<PackageScripts, TargetOptions>(
86+
pkgScripts,
87+
(command) => ({ command })
88+
)
89+
90+
await writeJson(commandsFilePath, serializedTargets)
91+
92+
this.logger.info(`Generated ${this.cfg.commandsFile} in ${projectCwd}`)
93+
return
94+
}
95+
96+
if (!options?.all) {
97+
this.logger.error(
98+
`The specified target type '${targetType}' is not allowed.
99+
It seems you may already have a '${this.cfg.commandsFile}' file in your project.`
100+
)
101+
}
102+
}
103+
104+
@Option({
105+
flags: '--all [boolean]',
106+
name: 'all'
107+
})
108+
public parseAll(all: string) {
109+
return this.utilityService.parseBoolean(all)
110+
}
111+
}

src/commands/run/run.command.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ import {
1010
} from 'nest-commander'
1111
import { dirname } from 'node:path'
1212
import { buildTargetInfoPrompt } from '@/commands/run/run.prompts'
13-
import { createIndependentTargetCommand } from '@/commands/run/utils/independent-target-command'
1413
import { LoggerService } from '@/logger'
1514
import type { AbstractPackageManager } from '@/pkg-manager'
1615
import { InjectPackageManager } from '@/pkg-manager'
1716
import { PackageManager, ROOT_PROJECT } from '@/pkg-manager/pkg-manager.consts'
1817
import { ResolverService } from '@/resolver/resolver.service'
1918
import { invariant } from '@/shared/misc'
19+
import { createIndependentTargetCommand } from './utils/independent-target-command'
2020

2121
export interface RunCommandOptions {
2222
args?: string

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,13 @@ export abstract class AbstractPackageManager {
3838
workspaces: WorkspaceProject[] = []
3939
): Promise<void> {
4040
const cwd = process.cwd()
41-
const { targets } = await this.resolver.resolveProjectTargets(cwd)
41+
const { targets, type } = await this.resolver.resolveProjectTargets(cwd)
4242

4343
const root = {
4444
name: ROOT_PROJECT,
4545
location: cwd,
46-
targets
46+
targets,
47+
type
4748
} satisfies WorkspaceProject
4849

4950
this.projects = [root, ...workspaces]

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ export class BunPackageManager extends AbstractPackageManager {
3636

3737
const workspaceName = scopedPkgJson.name ?? null
3838
const workspaceDir = dirname(pattern)
39-
const { targets } =
39+
const { targets, type } =
4040
await this.resolver.resolveProjectTargets(workspaceDir)
4141

42-
return { name: workspaceName, location: workspaceDir, targets }
42+
return { name: workspaceName, location: workspaceDir, targets, type }
4343
})
4444
)
4545

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,10 @@ export class NpmPackageManager extends AbstractPackageManager {
5050
const normalizedPath = dependency.resolved.replace(/^file:..\//, '')
5151
const absolutePath = resolve(cwd, normalizedPath)
5252

53-
const { targets } =
53+
const { targets, type } =
5454
await this.resolver.resolveProjectTargets(absolutePath)
5555

56-
return { name, location: absolutePath, targets }
56+
return { name, location: absolutePath, targets, type }
5757
})
5858
)
5959

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,10 @@ export class PnpmPackageManager extends AbstractPackageManager {
3131
const isRoot = pathEqual(path, process.cwd())
3232
if (isRoot) return null
3333

34-
const { targets } = await this.resolver.resolveProjectTargets(path)
34+
const { targets, type } =
35+
await this.resolver.resolveProjectTargets(path)
3536

36-
return { name, location: path, targets }
37+
return { name, location: path, targets, type }
3738
})
3839
)
3940

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ export class YarnBerryPackageManager extends AbstractPackageManager {
2020
const workspaces = await Promise.all(
2121
serializedLines.map(async (serializedMeta) => {
2222
const project = parseJson<WorkspaceProject>(serializedMeta)
23-
const { targets } = await this.resolver.resolveProjectTargets(
23+
const { targets, type } = await this.resolver.resolveProjectTargets(
2424
project.location
2525
)
2626

27-
return { ...project, targets }
27+
return { ...project, targets, type }
2828
})
2929
)
3030

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,10 @@ export class YarnPackageManager extends AbstractPackageManager {
3737
const yarnWorkspaces = await Promise.all(
3838
workspacesEntries.map(async ([name, metadata]) => {
3939
const location = toAbsolutePath(metadata.location)
40-
const { targets } = await this.resolver.resolveProjectTargets(location)
40+
const { targets, type } =
41+
await this.resolver.resolveProjectTargets(location)
4142

42-
return { name, location, targets }
43+
return { name, location, targets, type }
4344
})
4445
)
4546

src/pkg-manager/pkg-manager.types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
import type { TargetType } from '@/resolver/resolver.types'
12
import type { TargetOptions } from '@/resolver/targets/targets-resolver.schema'
23

34
export interface WorkspaceProject {
45
name: string | null
56
location: string
67
targets: TargetOptions
8+
type: TargetType
79
}
810

911
export interface RunCommandOptions {

0 commit comments

Comments
 (0)