@inspecto-dev/cli 0.2.0-alpha.4 → 0.2.0-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-test.log +16 -21
- package/CHANGELOG.md +12 -0
- package/README.md +58 -11
- package/bin/inspecto.js +5 -1
- package/dist/bin.d.ts +5 -1
- package/dist/bin.js +89 -50
- package/dist/{chunk-EUCQCD3Y.js → chunk-PDDFPQJS.js} +1954 -1053
- package/dist/index.d.ts +128 -2
- package/dist/index.js +15 -3
- package/package.json +2 -1
- package/src/bin.ts +139 -67
- package/src/commands/apply.ts +114 -0
- package/src/commands/detect.ts +59 -0
- package/src/commands/doctor.ts +225 -72
- package/src/commands/init.ts +106 -183
- package/src/commands/plan.ts +41 -0
- package/src/detect/build-tool.ts +107 -3
- package/src/index.ts +13 -2
- package/src/inject/ast-injector.ts +20 -9
- package/src/inject/extension.ts +3 -1
- package/src/inject/strategies/vite.ts +2 -1
- package/src/instructions.ts +60 -46
- package/src/onboarding/apply.ts +325 -0
- package/src/onboarding/context.ts +36 -0
- package/src/onboarding/planner.ts +278 -0
- package/src/prompts.ts +54 -11
- package/src/types.ts +95 -0
- package/src/utils/fs.ts +2 -1
- package/src/utils/logger.ts +9 -0
- package/src/utils/output.ts +40 -0
- package/tests/apply.test.ts +537 -0
- package/tests/ast-injector.test.ts +50 -0
- package/tests/build-tool.test.ts +3 -5
- package/tests/detect.test.ts +94 -0
- package/tests/doctor.test.ts +224 -0
- package/tests/init.test.ts +333 -0
- package/tests/instructions.test.ts +61 -0
- package/tests/logger.test.ts +100 -0
- package/tests/plan.test.ts +713 -0
- package/tests/workspace-build-tool.test.ts +75 -0
|
@@ -12,7 +12,8 @@ export class ViteStrategy implements InjectStrategy {
|
|
|
12
12
|
inject({ mod, detection }: InjectOptions): void {
|
|
13
13
|
addVitePlugin(mod, {
|
|
14
14
|
from: '@inspecto-dev/plugin',
|
|
15
|
-
constructor: '
|
|
15
|
+
constructor: 'inspecto',
|
|
16
|
+
imported: 'vitePlugin',
|
|
16
17
|
})
|
|
17
18
|
}
|
|
18
19
|
|
package/src/instructions.ts
CHANGED
|
@@ -2,54 +2,68 @@ import { log } from './utils/logger.js'
|
|
|
2
2
|
|
|
3
3
|
export function printNuxtManualInstructions() {
|
|
4
4
|
log.blank()
|
|
5
|
-
log.hint('
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
5
|
+
log.hint('Nuxt requires manual setup in the current version.')
|
|
6
|
+
log.hint('1. Update `nuxt.config.ts` to register the Inspecto Vite plugin:')
|
|
7
|
+
log.copyableCodeBlock([
|
|
8
|
+
"import { vitePlugin as inspecto } from '@inspecto-dev/plugin'",
|
|
9
|
+
'',
|
|
10
|
+
'export default defineNuxtConfig({',
|
|
11
|
+
' vite: {',
|
|
12
|
+
' plugins: [inspecto()],',
|
|
13
|
+
' },',
|
|
14
|
+
'})',
|
|
15
|
+
])
|
|
16
|
+
log.hint('2. Create `plugins/inspecto.client.ts` to mount `@inspecto-dev/core` in development:')
|
|
17
|
+
log.copyableCodeBlock([
|
|
18
|
+
'export default defineNuxtPlugin(() => {',
|
|
19
|
+
' if (import.meta.dev) {',
|
|
20
|
+
" import('@inspecto-dev/core').then(({ mountInspector }) => {",
|
|
21
|
+
' mountInspector()',
|
|
22
|
+
' })',
|
|
23
|
+
' }',
|
|
24
|
+
'})',
|
|
25
|
+
])
|
|
26
|
+
log.hint('3. Restart your Nuxt dev server after updating the config.')
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
export function printNextJsManualInstructions() {
|
|
27
30
|
log.blank()
|
|
28
|
-
log.hint('
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
},
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
31
|
+
log.hint('Next.js requires manual setup in the current version.')
|
|
32
|
+
log.hint('1. Update `next.config.mjs` to register the Inspecto webpack plugin:')
|
|
33
|
+
log.copyableCodeBlock([
|
|
34
|
+
"import { webpackPlugin as inspecto } from '@inspecto-dev/plugin'",
|
|
35
|
+
'',
|
|
36
|
+
"/** @type {import('next').NextConfig} */",
|
|
37
|
+
'const nextConfig = {',
|
|
38
|
+
' webpack: (config, { dev, isServer }) => {',
|
|
39
|
+
' if (dev && !isServer) {',
|
|
40
|
+
' config.plugins.push(inspecto())',
|
|
41
|
+
' }',
|
|
42
|
+
' return config',
|
|
43
|
+
' },',
|
|
44
|
+
'}',
|
|
45
|
+
'',
|
|
46
|
+
'export default nextConfig',
|
|
47
|
+
])
|
|
48
|
+
log.hint(
|
|
49
|
+
'2. Initialize `@inspecto-dev/core` from a client component such as `app/layout.tsx` or `pages/_app.tsx`:',
|
|
50
|
+
)
|
|
51
|
+
log.copyableCodeBlock([
|
|
52
|
+
"'use client'",
|
|
53
|
+
'',
|
|
54
|
+
"import { useEffect } from 'react'",
|
|
55
|
+
'',
|
|
56
|
+
'export default function RootLayout({ children }) {',
|
|
57
|
+
' useEffect(() => {',
|
|
58
|
+
" if (process.env.NODE_ENV !== 'production') {",
|
|
59
|
+
" import('@inspecto-dev/core').then(({ mountInspector }) => {",
|
|
60
|
+
" mountInspector({ serverUrl: 'http://127.0.0.1:5678' })",
|
|
61
|
+
' })',
|
|
62
|
+
' }',
|
|
63
|
+
' }, [])',
|
|
64
|
+
'',
|
|
65
|
+
' return <html><body>{children}</body></html>',
|
|
66
|
+
'}',
|
|
67
|
+
])
|
|
68
|
+
log.hint('3. Restart your Next.js dev server after updating the config.')
|
|
55
69
|
}
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import ora from 'ora'
|
|
3
|
+
import { getInstallCommand } from '../detect/package-manager.js'
|
|
4
|
+
import { injectPlugin } from '../inject/ast-injector.js'
|
|
5
|
+
import { installExtension } from '../inject/extension.js'
|
|
6
|
+
import { updateGitignore } from '../inject/gitignore.js'
|
|
7
|
+
import { shell } from '../utils/exec.js'
|
|
8
|
+
import { exists, readJSON, writeJSON } from '../utils/fs.js'
|
|
9
|
+
import { log } from '../utils/logger.js'
|
|
10
|
+
import type {
|
|
11
|
+
BuildToolDetection,
|
|
12
|
+
CommandStatus,
|
|
13
|
+
InstallLock,
|
|
14
|
+
Mutation,
|
|
15
|
+
PackageManager,
|
|
16
|
+
PlanResult,
|
|
17
|
+
} from '../types.js'
|
|
18
|
+
|
|
19
|
+
export interface ApplyOnboardingInput {
|
|
20
|
+
repoRoot: string
|
|
21
|
+
projectRoot: string
|
|
22
|
+
packageManager: PackageManager
|
|
23
|
+
supportedBuildTargets: BuildToolDetection[]
|
|
24
|
+
options: {
|
|
25
|
+
shared: boolean
|
|
26
|
+
skipInstall: boolean
|
|
27
|
+
dryRun: boolean
|
|
28
|
+
noExtension: boolean
|
|
29
|
+
quiet?: boolean | undefined
|
|
30
|
+
}
|
|
31
|
+
selectedIDE?: { ide: string; supported: boolean } | null | undefined
|
|
32
|
+
providerDefault?: string | undefined
|
|
33
|
+
manualConfigRequiredFor?: string | undefined
|
|
34
|
+
injectionSkippedRequiresManualConfig?: boolean | undefined
|
|
35
|
+
plan?: PlanResult | undefined
|
|
36
|
+
allowManualPlanApply?: boolean | undefined
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface ApplyReporter {
|
|
40
|
+
warn(text: string): void
|
|
41
|
+
success(text: string): void
|
|
42
|
+
error(text: string): void
|
|
43
|
+
hint(text: string): void
|
|
44
|
+
dryRun(text: string): void
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface ApplySpinner {
|
|
48
|
+
start(): void
|
|
49
|
+
succeed(text: string): void
|
|
50
|
+
fail(text: string): void
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface ApplyOnboardingResult {
|
|
54
|
+
status: CommandStatus
|
|
55
|
+
mutations: Mutation[]
|
|
56
|
+
postInstall: {
|
|
57
|
+
installFailed: boolean
|
|
58
|
+
injectionFailed: boolean
|
|
59
|
+
manualExtensionInstallNeeded: boolean
|
|
60
|
+
nextSteps: string[]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function resultStatus(nextSteps: string[]): CommandStatus {
|
|
65
|
+
return nextSteps.length > 0 ? 'warning' : 'ok'
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function manualPlanSteps(plan: PlanResult): string[] {
|
|
69
|
+
return [
|
|
70
|
+
...plan.blockers.map(blocker => blocker.message),
|
|
71
|
+
...plan.actions
|
|
72
|
+
.filter(action => action.type === 'manual_step')
|
|
73
|
+
.map(action => action.description),
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export async function applyOnboardingPlan(
|
|
78
|
+
input: ApplyOnboardingInput,
|
|
79
|
+
): Promise<ApplyOnboardingResult> {
|
|
80
|
+
return applyOnboardingPlanInternal(input)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function createReporter(quiet = false): ApplyReporter {
|
|
84
|
+
if (quiet) {
|
|
85
|
+
return {
|
|
86
|
+
warn() {},
|
|
87
|
+
success() {},
|
|
88
|
+
error() {},
|
|
89
|
+
hint() {},
|
|
90
|
+
dryRun() {},
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
warn(text: string) {
|
|
96
|
+
log.warn(text)
|
|
97
|
+
},
|
|
98
|
+
success(text: string) {
|
|
99
|
+
log.success(text)
|
|
100
|
+
},
|
|
101
|
+
error(text: string) {
|
|
102
|
+
log.error(text)
|
|
103
|
+
},
|
|
104
|
+
hint(text: string) {
|
|
105
|
+
log.hint(text)
|
|
106
|
+
},
|
|
107
|
+
dryRun(text: string) {
|
|
108
|
+
log.dryRun(text)
|
|
109
|
+
},
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function createSpinner(text: string, quiet = false): ApplySpinner {
|
|
114
|
+
if (quiet) {
|
|
115
|
+
return {
|
|
116
|
+
start() {},
|
|
117
|
+
succeed() {},
|
|
118
|
+
fail() {},
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const spinner = ora(text)
|
|
123
|
+
return {
|
|
124
|
+
start() {
|
|
125
|
+
spinner.start()
|
|
126
|
+
},
|
|
127
|
+
succeed(successText: string) {
|
|
128
|
+
spinner.succeed(successText)
|
|
129
|
+
},
|
|
130
|
+
fail(failureText: string) {
|
|
131
|
+
spinner.fail(failureText)
|
|
132
|
+
},
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function applyOnboardingPlanInternal(
|
|
137
|
+
input: ApplyOnboardingInput,
|
|
138
|
+
): Promise<ApplyOnboardingResult> {
|
|
139
|
+
const reporter = createReporter(input.options.quiet)
|
|
140
|
+
|
|
141
|
+
if (input.plan && input.plan.strategy !== 'supported' && !input.allowManualPlanApply) {
|
|
142
|
+
return {
|
|
143
|
+
status: input.plan.status,
|
|
144
|
+
mutations: [],
|
|
145
|
+
postInstall: {
|
|
146
|
+
installFailed: false,
|
|
147
|
+
injectionFailed: false,
|
|
148
|
+
manualExtensionInstallNeeded: false,
|
|
149
|
+
nextSteps: manualPlanSteps(input.plan),
|
|
150
|
+
},
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const mutations: Mutation[] = []
|
|
155
|
+
const settingsDir = path.join(input.projectRoot, '.inspecto')
|
|
156
|
+
const settingsFileName = input.options.shared ? 'settings.json' : 'settings.local.json'
|
|
157
|
+
const promptsFileName = input.options.shared ? 'prompts.json' : 'prompts.local.json'
|
|
158
|
+
const settingsPath = path.join(settingsDir, settingsFileName)
|
|
159
|
+
const promptsPath = path.join(settingsDir, promptsFileName)
|
|
160
|
+
const installCmd = getInstallCommand(
|
|
161
|
+
input.packageManager,
|
|
162
|
+
'@inspecto-dev/plugin @inspecto-dev/core',
|
|
163
|
+
)
|
|
164
|
+
const nextSteps: string[] = []
|
|
165
|
+
|
|
166
|
+
let installFailed = false
|
|
167
|
+
if (input.options.skipInstall) {
|
|
168
|
+
reporter.warn('Skipping dependency installation (--skip-install)')
|
|
169
|
+
} else if (input.options.dryRun) {
|
|
170
|
+
reporter.dryRun(`Would run: ${installCmd}`)
|
|
171
|
+
} else {
|
|
172
|
+
const spinner = createSpinner(
|
|
173
|
+
`Installing devDependencies via: ${installCmd}`,
|
|
174
|
+
input.options.quiet,
|
|
175
|
+
)
|
|
176
|
+
try {
|
|
177
|
+
spinner.start()
|
|
178
|
+
await shell(installCmd, input.projectRoot)
|
|
179
|
+
spinner.succeed('Dependencies installed successfully')
|
|
180
|
+
reporter.success('Installed @inspecto-dev/plugin and @inspecto-dev/core as devDependencies')
|
|
181
|
+
mutations.push({ type: 'dependency_added', name: '@inspecto-dev/plugin', dev: true })
|
|
182
|
+
mutations.push({ type: 'dependency_added', name: '@inspecto-dev/core', dev: true })
|
|
183
|
+
} catch (error: any) {
|
|
184
|
+
spinner.fail('Dependency installation failed')
|
|
185
|
+
installFailed = true
|
|
186
|
+
reporter.error(`Failed to install dependency: ${error?.message || 'Unknown error'}`)
|
|
187
|
+
reporter.hint(`Run manually in ${input.projectRoot}: ${installCmd}`)
|
|
188
|
+
reporter.hint(
|
|
189
|
+
'Setup will continue without dependencies, but Inspecto may not run until installation succeeds.',
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
let injectionFailed = Boolean(input.injectionSkippedRequiresManualConfig)
|
|
195
|
+
for (const target of input.supportedBuildTargets) {
|
|
196
|
+
const result = await injectPlugin(input.repoRoot, target, input.options.dryRun)
|
|
197
|
+
if (result.success) {
|
|
198
|
+
mutations.push(...result.mutations)
|
|
199
|
+
} else {
|
|
200
|
+
injectionFailed = true
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (await exists(settingsPath)) {
|
|
205
|
+
const existingSettings = await readJSON(settingsPath)
|
|
206
|
+
if (existingSettings === null) {
|
|
207
|
+
reporter.warn(`.inspecto/${settingsFileName} exists but contains invalid JSON`)
|
|
208
|
+
reporter.hint('Please fix the syntax errors manually, or delete it and re-run init')
|
|
209
|
+
nextSteps.push(`Fix .inspecto/${settingsFileName} or delete it and rerun Inspecto setup.`)
|
|
210
|
+
} else {
|
|
211
|
+
reporter.success(`.inspecto/${settingsFileName} already exists (skipped)`)
|
|
212
|
+
}
|
|
213
|
+
} else {
|
|
214
|
+
const defaultSettings: Record<string, unknown> = {}
|
|
215
|
+
|
|
216
|
+
if (input.selectedIDE?.supported) {
|
|
217
|
+
defaultSettings.ide =
|
|
218
|
+
input.selectedIDE.ide.toLowerCase() === 'vscode'
|
|
219
|
+
? 'vscode'
|
|
220
|
+
: input.selectedIDE.ide.toLowerCase()
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (input.providerDefault) {
|
|
224
|
+
defaultSettings['provider.default'] = input.providerDefault
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (input.options.dryRun) {
|
|
228
|
+
reporter.dryRun(`Would create .inspecto/${settingsFileName}`)
|
|
229
|
+
} else {
|
|
230
|
+
await writeJSON(settingsPath, defaultSettings)
|
|
231
|
+
reporter.success(`Created .inspecto/${settingsFileName}`)
|
|
232
|
+
mutations.push({ type: 'file_created', path: `.inspecto/${settingsFileName}` })
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (await exists(promptsPath)) {
|
|
237
|
+
reporter.success(`.inspecto/${promptsFileName} already exists (skipped)`)
|
|
238
|
+
} else if (input.options.dryRun) {
|
|
239
|
+
reporter.dryRun(`Would create .inspecto/${promptsFileName}`)
|
|
240
|
+
} else {
|
|
241
|
+
await writeJSON(promptsPath, [])
|
|
242
|
+
reporter.success(`Created .inspecto/${promptsFileName}`)
|
|
243
|
+
mutations.push({ type: 'file_created', path: `.inspecto/${promptsFileName}` })
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (!input.options.dryRun) {
|
|
247
|
+
await updateGitignore(input.projectRoot, input.options.shared, input.options.dryRun)
|
|
248
|
+
mutations.push({
|
|
249
|
+
type: 'file_modified',
|
|
250
|
+
path: '.gitignore',
|
|
251
|
+
description: 'Appended .inspecto/ ignore rules',
|
|
252
|
+
})
|
|
253
|
+
} else {
|
|
254
|
+
reporter.dryRun('Would update .gitignore')
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (!input.options.dryRun && mutations.length > 0) {
|
|
258
|
+
const lock: InstallLock = {
|
|
259
|
+
version: '1.0.0',
|
|
260
|
+
created_at: new Date().toISOString(),
|
|
261
|
+
mutations,
|
|
262
|
+
}
|
|
263
|
+
await writeJSON(path.join(settingsDir, 'install.lock'), lock)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const shouldInstallExt =
|
|
267
|
+
!input.options.noExtension && (!input.selectedIDE || input.selectedIDE.supported)
|
|
268
|
+
let manualExtensionInstallNeeded = false
|
|
269
|
+
|
|
270
|
+
if (input.options.noExtension) {
|
|
271
|
+
reporter.warn('Skipping IDE extension (--no-extension)')
|
|
272
|
+
} else if (shouldInstallExt) {
|
|
273
|
+
const extMutation = await installExtension(input.options.dryRun, input.selectedIDE?.ide)
|
|
274
|
+
if (extMutation && !input.options.dryRun) {
|
|
275
|
+
mutations.push(extMutation)
|
|
276
|
+
|
|
277
|
+
if (extMutation.manual_action_required) {
|
|
278
|
+
manualExtensionInstallNeeded = true
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const lockPath = path.join(settingsDir, 'install.lock')
|
|
282
|
+
const lock = await readJSON<InstallLock>(lockPath)
|
|
283
|
+
if (lock) {
|
|
284
|
+
lock.mutations = mutations
|
|
285
|
+
await writeJSON(lockPath, lock)
|
|
286
|
+
}
|
|
287
|
+
} else if (extMutation === null && !input.options.dryRun) {
|
|
288
|
+
manualExtensionInstallNeeded = true
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (!input.options.dryRun) {
|
|
293
|
+
if (installFailed) {
|
|
294
|
+
nextSteps.push(`Install dependencies manually in ${input.projectRoot}: ${installCmd}`)
|
|
295
|
+
}
|
|
296
|
+
if (injectionFailed) {
|
|
297
|
+
nextSteps.push(
|
|
298
|
+
'Plugin injection skipped. Follow manual instructions printed above to update your config.',
|
|
299
|
+
)
|
|
300
|
+
}
|
|
301
|
+
if (manualExtensionInstallNeeded) {
|
|
302
|
+
nextSteps.push('Install the Inspecto IDE extension manually')
|
|
303
|
+
}
|
|
304
|
+
if (input.manualConfigRequiredFor === 'Nuxt') {
|
|
305
|
+
nextSteps.push(
|
|
306
|
+
'Nuxt detected—please follow the Nuxt instructions printed above to finish setup.',
|
|
307
|
+
)
|
|
308
|
+
} else if (input.manualConfigRequiredFor === 'Next.js') {
|
|
309
|
+
nextSteps.push(
|
|
310
|
+
'Next.js detected—please follow the Next.js instructions printed above to finish setup.',
|
|
311
|
+
)
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return {
|
|
316
|
+
status: resultStatus(nextSteps),
|
|
317
|
+
mutations,
|
|
318
|
+
postInstall: {
|
|
319
|
+
installFailed,
|
|
320
|
+
injectionFailed,
|
|
321
|
+
manualExtensionInstallNeeded,
|
|
322
|
+
nextSteps,
|
|
323
|
+
},
|
|
324
|
+
}
|
|
325
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { detectBuildTools } from '../detect/build-tool.js'
|
|
2
|
+
import { detectFrameworks } from '../detect/framework.js'
|
|
3
|
+
import { detectIDE } from '../detect/ide.js'
|
|
4
|
+
import { detectPackageManager } from '../detect/package-manager.js'
|
|
5
|
+
import { detectProviders } from '../detect/provider.js'
|
|
6
|
+
import type { OnboardingContext } from '../types.js'
|
|
7
|
+
|
|
8
|
+
export async function buildOnboardingContext(root: string): Promise<OnboardingContext> {
|
|
9
|
+
const [packageManager, buildTools, frameworks, ides, providers] = await Promise.all([
|
|
10
|
+
detectPackageManager(root),
|
|
11
|
+
detectBuildTools(root),
|
|
12
|
+
detectFrameworks(root),
|
|
13
|
+
detectIDE(root),
|
|
14
|
+
detectProviders(root),
|
|
15
|
+
])
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
root,
|
|
19
|
+
packageManager,
|
|
20
|
+
buildTools: {
|
|
21
|
+
supported: buildTools.supported,
|
|
22
|
+
unsupported: buildTools.unsupported,
|
|
23
|
+
},
|
|
24
|
+
frameworks: {
|
|
25
|
+
supported: frameworks.supported,
|
|
26
|
+
unsupported: frameworks.unsupported.map(item => item.name),
|
|
27
|
+
},
|
|
28
|
+
ides: ides.detected.map(({ ide, supported }) => ({ ide, supported })),
|
|
29
|
+
providers: providers.detected.map(({ id, label, supported, preferredMode }) => ({
|
|
30
|
+
id,
|
|
31
|
+
label,
|
|
32
|
+
supported,
|
|
33
|
+
preferredMode,
|
|
34
|
+
})),
|
|
35
|
+
}
|
|
36
|
+
}
|