Skip to content

Commit 1d85809

Browse files
committed
refactor: extract operations layer and expand feature definitions
Add meow dependency for CLI arg parsing and remove ink-spawn. Expand config.ts with full feature definitions and update utils for the new config structure. Extract reusable operations (cloneRepo, createEnvFile, installPackages, cleanupFiles) with a shared exec helper.
1 parent 9d43f35 commit 1d85809

10 files changed

Lines changed: 262 additions & 91 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
"ink-gradient": "^3.0.0",
2323
"ink-link": "^4.1.0",
2424
"ink-select-input": "^6.2.0",
25-
"ink-spawn": "^0.1.4",
2625
"ink-text-input": "^6.0.0",
26+
"meow": "^14.1.0",
2727
"react": "^18.3.1"
2828
},
2929
"devDependencies": {

pnpm-lock.yaml

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

source/constants/config.ts

Lines changed: 59 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,62 @@
11
export const repoUrl = 'https://github.com/BootNodeDev/dAppBooster.git'
22

3-
export const featurePackages: {
4-
[key: string]: string[]
5-
} = {
6-
subgraph: [
7-
'@bootnodedev/db-subgraph',
8-
'graphql graphql-request',
9-
'@graphql-codegen/cli',
10-
'@graphql-typed-document-node/core',
11-
],
12-
typedoc: [
13-
'typedoc',
14-
'typedoc-github-theme',
15-
'typedoc-plugin-inline-sources',
16-
'typedoc-plugin-missing-exports',
17-
'typedoc-plugin-rename-defaults',
18-
],
19-
vocs: ['vocs'],
20-
husky: ['husky', 'lint-staged', '@commitlint/cli', '@commitlint/config-conventional'],
3+
export type FeatureName = 'demo' | 'subgraph' | 'typedoc' | 'vocs' | 'husky'
4+
5+
export type FeatureDefinition = {
6+
description: string
7+
label: string
8+
packages: string[]
9+
default: boolean
10+
postInstall?: string[]
11+
}
12+
13+
export const featureDefinitions: Record<FeatureName, FeatureDefinition> = {
14+
demo: {
15+
description: 'Component demos and example pages',
16+
label: 'Component Demos',
17+
packages: [],
18+
default: true,
19+
},
20+
subgraph: {
21+
description: 'TheGraph subgraph integration',
22+
label: 'Subgraph support',
23+
packages: [
24+
'@bootnodedev/db-subgraph',
25+
'graphql',
26+
'graphql-request',
27+
'@graphql-codegen/cli',
28+
'@graphql-typed-document-node/core',
29+
],
30+
default: true,
31+
postInstall: [
32+
'Provide your own API key for PUBLIC_SUBGRAPHS_API_KEY in .env.local',
33+
'Run pnpm subgraph-codegen from the project folder',
34+
],
35+
},
36+
typedoc: {
37+
description: 'TypeDoc API documentation generation',
38+
label: 'Typedoc documentation support',
39+
packages: [
40+
'typedoc',
41+
'typedoc-github-theme',
42+
'typedoc-plugin-inline-sources',
43+
'typedoc-plugin-missing-exports',
44+
'typedoc-plugin-rename-defaults',
45+
],
46+
default: true,
47+
},
48+
vocs: {
49+
description: 'Vocs documentation site',
50+
label: 'Vocs documentation support',
51+
packages: ['vocs'],
52+
default: true,
53+
},
54+
husky: {
55+
description: 'Git hooks with Husky, lint-staged, and commitlint',
56+
label: 'Husky Git hooks support',
57+
packages: ['husky', 'lint-staged', '@commitlint/cli', '@commitlint/config-conventional'],
58+
default: true,
59+
},
2160
}
61+
62+
export const featureNames = Object.keys(featureDefinitions) as FeatureName[]

source/operations/cleanupFiles.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { readFileSync, writeFileSync } from 'node:fs'
2+
import { resolve } from 'node:path'
3+
import type { FeatureName } from '../constants/config.js'
4+
import type { InstallationType } from '../types/types.js'
5+
import { isFeatureSelected } from '../utils/utils.js'
6+
import { exec } from './exec.js'
7+
8+
function patchPackageJson(projectFolder: string, features: FeatureName[]): void {
9+
const packageJsonPath = resolve(projectFolder, 'package.json')
10+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'))
11+
12+
if (!isFeatureSelected('subgraph', features)) {
13+
packageJson.scripts['subgraph-codegen'] = undefined
14+
}
15+
16+
if (!isFeatureSelected('typedoc', features)) {
17+
packageJson.scripts['typedoc:build'] = undefined
18+
}
19+
20+
if (!isFeatureSelected('vocs', features)) {
21+
packageJson.scripts['docs:build'] = undefined
22+
packageJson.scripts['docs:dev'] = undefined
23+
packageJson.scripts['docs:preview'] = undefined
24+
}
25+
26+
if (!isFeatureSelected('husky', features)) {
27+
packageJson.scripts.prepare = undefined
28+
}
29+
30+
writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`)
31+
}
32+
33+
async function cleanupDemo(projectFolder: string): Promise<void> {
34+
await exec('rm -rf src/components/pageComponents/home', { cwd: projectFolder })
35+
await exec('mkdir -p src/components/pageComponents/home', { cwd: projectFolder })
36+
await exec('cp .install-files/home/index.tsx src/components/pageComponents/home/', {
37+
cwd: projectFolder,
38+
})
39+
}
40+
41+
async function cleanupSubgraph(projectFolder: string, features: FeatureName[]): Promise<void> {
42+
await exec('rm -rf src/subgraphs', { cwd: projectFolder })
43+
44+
if (isFeatureSelected('demo', features)) {
45+
const homeFolder = 'src/components/pageComponents/home'
46+
47+
await exec(`rm -rf ${homeFolder}/Examples/demos/subgraphs`, { cwd: projectFolder })
48+
await exec(`rm ${homeFolder}/Examples/index.tsx`, { cwd: projectFolder })
49+
await exec(`cp .install-files/home/Examples/index.tsx ${homeFolder}/Examples/index.tsx`, {
50+
cwd: projectFolder,
51+
})
52+
}
53+
}
54+
55+
async function cleanupTypedoc(projectFolder: string): Promise<void> {
56+
await exec('rm typedoc.json', { cwd: projectFolder })
57+
}
58+
59+
async function cleanupVocs(projectFolder: string): Promise<void> {
60+
await exec('rm vocs.config.ts', { cwd: projectFolder })
61+
await exec('rm -rf docs', { cwd: projectFolder })
62+
}
63+
64+
async function cleanupHusky(projectFolder: string): Promise<void> {
65+
await exec('rm -rf .husky', { cwd: projectFolder })
66+
await exec('rm .lintstagedrc.mjs', { cwd: projectFolder })
67+
await exec('rm commitlint.config.js', { cwd: projectFolder })
68+
}
69+
70+
export async function cleanupFiles(
71+
projectFolder: string,
72+
mode: InstallationType,
73+
features: FeatureName[] = [],
74+
): Promise<void> {
75+
if (mode === 'custom') {
76+
if (!isFeatureSelected('demo', features)) {
77+
await cleanupDemo(projectFolder)
78+
}
79+
80+
if (!isFeatureSelected('subgraph', features)) {
81+
await cleanupSubgraph(projectFolder, features)
82+
}
83+
84+
if (!isFeatureSelected('typedoc', features)) {
85+
await cleanupTypedoc(projectFolder)
86+
}
87+
88+
if (!isFeatureSelected('vocs', features)) {
89+
await cleanupVocs(projectFolder)
90+
}
91+
92+
if (!isFeatureSelected('husky', features)) {
93+
await cleanupHusky(projectFolder)
94+
}
95+
96+
patchPackageJson(projectFolder, features)
97+
}
98+
99+
await exec('rm -rf .install-files', { cwd: projectFolder })
100+
}

source/operations/cloneRepo.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { repoUrl } from '../constants/config.js'
2+
import { getProjectFolder } from '../utils/utils.js'
3+
import { exec } from './exec.js'
4+
5+
export async function cloneRepo(projectName: string): Promise<void> {
6+
const projectFolder = getProjectFolder(projectName)
7+
8+
await exec(`git clone --depth 1 --no-checkout ${repoUrl} ${projectName}`)
9+
await exec('git fetch --tags', { cwd: projectFolder })
10+
await exec('git checkout $(git describe --tags $(git rev-list --tags --max-count=1))', {
11+
cwd: projectFolder,
12+
})
13+
await exec('rm -rf .git', { cwd: projectFolder })
14+
await exec('git init', { cwd: projectFolder })
15+
}

source/operations/createEnvFile.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { exec } from './exec.js'
2+
3+
export async function createEnvFile(projectFolder: string): Promise<void> {
4+
await exec('cp .env.example .env.local', { cwd: projectFolder })
5+
}

source/operations/exec.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { exec as nodeExec } from 'node:child_process'
2+
import { promisify } from 'node:util'
3+
4+
const execAsync = promisify(nodeExec)
5+
6+
export async function exec(command: string, options: { cwd?: string } = {}): Promise<string> {
7+
try {
8+
const { stdout } = await execAsync(command, {
9+
cwd: options.cwd,
10+
})
11+
12+
return stdout.trim()
13+
} catch (error: unknown) {
14+
const execError = error as { stderr?: string; message?: string }
15+
const message = execError.stderr?.trim() || execError.message || 'Unknown error'
16+
throw new Error(message)
17+
}
18+
}

source/operations/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export { cloneRepo } from './cloneRepo.js'
2+
export { createEnvFile } from './createEnvFile.js'
3+
export { installPackages } from './installPackages.js'
4+
export { cleanupFiles } from './cleanupFiles.js'

0 commit comments

Comments
 (0)