@vtex/fsp-cli
Version:
A VTEX CLI
132 lines (106 loc) • 3.38 kB
text/typescript
import { readFileSync } from 'node:fs'
import path from 'node:path'
import type { Command } from '@oclif/core'
import { type ModuleConfig, loadConfig } from '@vtex/fsp-config'
import type { PackageJson } from 'type-fest'
/**
* Maps the module name to the correct npm package
*/
export const moduleCliMap: Record<string, string> = {
checkout: '@vtex/checkout',
discovery: '@faststore/cli',
'sales-app': '@vtex/sales-app',
}
const moduleCLIPathMap: Record<string, string> = {
'@vtex/checkout': '@vtex/checkout/cli',
'@faststore/cli': '@faststore/cli',
'@vtex/sales-app': '@vtex/sales-app/cli',
}
export const availableModules = Object.keys(moduleCliMap)
/**
* Load modules from the config file of a given account
* @param account Account of the config to load the modules
* @param moduleFilterFn Filter modules to be loaded. It loads all modules by default
*/
export async function loadModules(
account: string,
moduleFilterFn: (module: string) => boolean = () => true
) {
const { stores } = await loadConfig()
const accountConfigs = stores[account]
if (!accountConfigs) {
const availableAccounts = Object.keys(stores).join(', ')
throw new Error(
`Could not find account "${account}". Found accounts: ${availableAccounts}`
)
}
const modules = Object.keys(accountConfigs)
.filter(moduleFilterFn)
.map((module) => {
const moduleConfig = accountConfigs[module]
const cli = moduleConfig?.cli ?? moduleCliMap[module]
if (!cli) {
throw new Error('CLI not found! Provide a valid module or a CLI')
}
return {
...accountConfigs[module],
cli,
}
})
return await Promise.all(modules.map((module) => loadModule(module)))
}
async function loadModule(module: Module): Promise<LoadedModule> {
const foundDirectory = path.join(process.cwd(), module.path)
if (!foundDirectory) {
throw new Error('Module not found')
}
const loadedCli = await load(module)
return { ...module, loadedCli }
}
export async function load(module: Module): Promise<ModulePackageExports> {
let rootPackageJson: PackageJson = {}
try {
rootPackageJson = JSON.parse(
readFileSync(path.join(process.cwd(), 'package.json')).toString()
)
} catch {
throw new Error('Could not find package.json')
}
if (!rootPackageJson.devDependencies?.[module.cli]) {
throw new Error(
`You must add ${module.cli} to your devDependencies and install it`
)
}
try {
const importedModule = await import(moduleCLIPathMap[module.cli])
const isEsm = !!importedModule.__esModule
return isEsm ? importedModule.default : importedModule
} catch {
throw new Error(`Could not import module ${module.cli}`)
}
}
export interface CommandList {
build: Command.Class
dev: Command.Class
create: Command.Class
serve: Command.Class
[key: string]: Command.Class | undefined
}
interface HookList {
preDev?: () => Promise<void>
postDev?: () => Promise<void>
preBuild?: () => Promise<void>
postBuild?: () => Promise<void>
preServe?: () => Promise<void>
postServe?: () => Promise<void>
}
export interface LoadedModule extends Module {
loadedCli: ModulePackageExports
}
export interface ModulePackageExports {
commands: CommandList
hooks?: HookList
}
export interface Module extends ModuleConfig {
cli: string
}