Skip to content

Commit 4436f5b

Browse files
committed
feat: custom configuration
1 parent c1e5487 commit 4436f5b

File tree

13 files changed

+135
-20
lines changed

13 files changed

+135
-20
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
},
5454
"devDependencies": {
5555
"@antfu/ni": "^0.21.12",
56+
"@types/ini": "^4.1.0",
5657
"@types/inquirer": "^9.0.7",
5758
"@types/node": "^20.10.7",
5859
"@types/which": "^3.0.3",

pnpm-lock.yaml

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

shims.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
declare module 'inquirer-tree-prompt'
22
declare module 'inquirer-search-list'
33
declare module 'envfile'
4+
5+
declare namespace NodeJS {
6+
interface Process {
7+
GX_CONFIG_PATH: string
8+
}
9+
}

src/app/app.module.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { Module } from '@nestjs/common'
22
import { Commands } from '@/commands'
3+
import { ConfigModule } from '@/config'
34
import { LoggerModule } from '@/logger'
45
import { PackageManagerModule } from '@/pkg-manager'
56

67
@Module({
7-
imports: [PackageManagerModule, LoggerModule],
8+
imports: [PackageManagerModule, LoggerModule, ConfigModule],
89
providers: [...Commands]
910
})
1011
export class AppModule {}

src/commands/run/run.command.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ensureDir } from '@neodx/fs'
22
import { hasOwn, isEmpty, isObject } from '@neodx/std'
33
import { Inject } from '@nestjs/common'
4-
import { execaCommand } from 'execa'
4+
import { execaCommand as $ } from 'execa'
55
import { Command, CommandRunner, Option } from 'nest-commander'
66
import { dirname } from 'node:path'
77
import { buildTargetInfoPrompt } from '@/commands/run/run.prompts'
@@ -102,7 +102,7 @@ export class RunCommand extends CommandRunner {
102102
{ defaultArgs: options.args, projectCwd }
103103
)
104104

105-
await execaCommand(`${command} ${args}`, {
105+
await $(`${command} ${args}`, {
106106
cwd,
107107
env
108108
})

src/config/config.module.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { Global, Module } from '@nestjs/common'
2+
import { ConfigService } from './config.service'
3+
4+
@Global()
5+
@Module({
6+
controllers: [],
7+
providers: [ConfigService],
8+
exports: [ConfigService]
9+
})
10+
export class ConfigModule {}

src/config/config.schema.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { z } from 'zod'
2+
3+
export const ConfigSchema = z.object({
4+
commandsFile: z.string().default('targets.json'),
5+
preferredResolvingOrder: z
6+
.array(z.union([z.literal('package-scripts'), z.literal('targets')]))
7+
.default(['targets', 'package-scripts'])
8+
})
9+
10+
function createConfigValues() {
11+
return class ConfigValues {} as {
12+
new (): z.infer<typeof ConfigSchema>
13+
}
14+
}
15+
16+
export class ConfigValues extends createConfigValues() {}

src/config/config.service.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { Inject, Injectable } from '@nestjs/common'
2+
import ini from 'ini'
3+
import { existsSync, readFileSync } from 'node:fs'
4+
import { resolve } from 'node:path'
5+
import process from 'node:process'
6+
import { ConfigSchema, ConfigValues } from '@/config/config.schema'
7+
import { LoggerService } from '@/logger'
8+
9+
@Injectable()
10+
export class ConfigService extends ConfigValues {
11+
constructor(@Inject(LoggerService) private readonly logger: LoggerService) {
12+
super()
13+
Object.assign(this, this.config)
14+
}
15+
16+
private get config() {
17+
const userConfig = existsSync(this.configPath)
18+
? ini.parse(readFileSync(this.configPath).toString())
19+
: {}
20+
21+
try {
22+
return ConfigSchema.parse(userConfig)
23+
} catch (error) {
24+
this.logger.error(error)
25+
process.exit(1)
26+
}
27+
}
28+
29+
private get configPath() {
30+
const customRcPath = process.env.GX_CONFIG_PATH
31+
32+
const isWindows = process.platform === 'win32'
33+
const home = isWindows ? process.env.USERPROFILE : process.env.HOME
34+
35+
const defaultRcPath = resolve(home ?? '~/', '.gxrc')
36+
const projectRcPath = resolve(process.cwd(), '.gxrc')
37+
38+
const baseRcPath = customRcPath ?? defaultRcPath
39+
40+
return existsSync(projectRcPath) ? projectRcPath : baseRcPath
41+
}
42+
}

src/config/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { ConfigModule } from './config.module'
2+
export { ConfigService } from './config.service'

src/resolver/resolver.service.ts

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,50 @@
1-
import { isNull } from '@neodx/std'
2-
import { Injectable } from '@nestjs/common'
1+
import { entries, sortObjectByOrder } from '@neodx/std'
2+
import { Inject, Injectable } from '@nestjs/common'
3+
import { ConfigService } from '@/config'
4+
import { LoggerService } from '@/logger'
35
import type { PackageJsonResolverService } from '@/resolver/package/package.resolver.service'
4-
import type { ResolvedTargets } from '@/resolver/resolver.types'
6+
import type { ResolvedTargets, TargetType } from '@/resolver/resolver.types'
7+
import type { TargetOptions } from '@/resolver/targets/targets-resolver.schema'
58
import type { TargetsResolverService } from '@/resolver/targets/targets-resolver.service'
69

710
@Injectable()
811
export class ResolverService {
912
constructor(
1013
private readonly targetsResolver: TargetsResolverService,
11-
private readonly pkgResolver: PackageJsonResolverService
14+
private readonly pkgResolver: PackageJsonResolverService,
15+
@Inject(ConfigService) private readonly cfg: ConfigService,
16+
@Inject(LoggerService) private readonly logger: LoggerService
1217
) {}
1318

1419
public async resolveProjectTargets(cwd: string): Promise<ResolvedTargets> {
15-
const targets = await this.targetsResolver.resolveProjectTargets(cwd)
20+
const sortedResolvers = sortObjectByOrder(
21+
this.resolverMethods,
22+
this.cfg.preferredResolvingOrder
23+
)
1624

17-
if (isNull(targets)) {
18-
return {
19-
type: 'package-scripts',
20-
targets: await this.pkgResolver.resolvePackageJsonScripts(cwd)
25+
for (const [type, resolveTargets] of entries(sortedResolvers)) {
26+
const targets = await resolveTargets.call(this, cwd)
27+
28+
if (targets) {
29+
return { type, targets }
2130
}
2231
}
2332

33+
this.logger.error(
34+
`Error occurred while resolving project targets.
35+
Please check if preferredResolvingOrder is set correctly`
36+
)
37+
process.exit(1)
38+
}
39+
40+
private get resolverMethods(): Record<
41+
TargetType,
42+
(cwd: string) => Promise<TargetOptions | null>
43+
> {
2444
return {
25-
type: 'targets',
26-
targets
45+
'package-scripts': this.pkgResolver.resolvePackageJsonScripts,
46+
// eslint-disable-next-line prettier/prettier
47+
'targets': this.targetsResolver.resolveProjectTargets
2748
}
2849
}
2950
}

src/resolver/resolver.types.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
import type { ConfigValues } from '@/config/config.schema'
12
import type { TargetOptions } from '@/resolver/targets/targets-resolver.schema'
3+
import type { InferArrayItem } from '@/shared/types'
4+
5+
export type TargetType = InferArrayItem<ConfigValues['preferredResolvingOrder']>
26

37
export interface ResolvedTargets {
4-
type: 'package-scripts' | 'targets'
8+
type: TargetType
59
targets: TargetOptions
610
}

src/resolver/targets/targets-resolver.service.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { AnyRecord } from '@neodx/std'
33
import { Inject, Injectable } from '@nestjs/common'
44
import { resolve } from 'node:path'
55
import { z } from 'zod'
6+
import { ConfigService } from '@/config'
67
import { LoggerService } from '@/logger'
78
import { readJson } from '@/shared/json'
89
import type { TargetOptions } from './targets-resolver.schema'
@@ -11,13 +12,14 @@ import { TargetsSchema } from './targets-resolver.schema'
1112
@Injectable()
1213
export class TargetsResolverService {
1314
constructor(
14-
@Inject(LoggerService) private readonly loggerService: LoggerService
15+
@Inject(LoggerService) private readonly logger: LoggerService,
16+
@Inject(ConfigService) private readonly cfg: ConfigService
1517
) {}
1618

1719
public async resolveProjectTargets(
1820
projectCwd: string
1921
): Promise<TargetOptions | null> {
20-
const targetJsonPath = resolve(projectCwd, 'commands.json')
22+
const targetJsonPath = resolve(projectCwd, this.cfg.commandsFile)
2123

2224
if (!exists(targetJsonPath)) return null
2325

@@ -36,14 +38,14 @@ export class TargetsResolverService {
3638
const isZodError = error_ instanceof z.ZodError
3739

3840
if (!isZodError) {
39-
this.loggerService.error('Unknown error parsing commands.json')
41+
this.logger.error('Unknown error parsing commands.json')
4042
throw error_
4143
}
4244

43-
this.loggerService.error(
45+
this.logger.error(
4446
`Invalid commands.json file. (${projectPath}) See below for detailed info. \n`
4547
)
46-
this.loggerService.error(error_.format())
48+
this.logger.error(error_.format())
4749

4850
process.exit(1)
4951
}

src/shared/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export type InferArrayItem<T extends unknown[]> = T extends (infer S)[]
2+
? S
3+
: never

0 commit comments

Comments
 (0)