@inspecto-dev/cli 0.3.4 → 0.3.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 +7 -7
- package/.turbo/turbo-test.log +205 -7275
- package/CHANGELOG.md +14 -0
- package/dist/bin.js +32 -1
- package/dist/{chunk-2MOEVONN.js → chunk-7ABJRH3F.js} +1232 -145
- package/dist/index.d.ts +62 -4
- package/dist/index.js +7 -1
- package/package.json +2 -2
- package/src/bin.ts +45 -0
- package/src/commands/dev-config.ts +109 -0
- package/src/commands/doctor.ts +162 -9
- package/src/commands/init.ts +10 -3
- package/src/commands/integration-automation.ts +2 -0
- package/src/commands/integration-host-ide.ts +18 -15
- package/src/commands/integration-install.ts +1 -1
- package/src/commands/onboard.ts +72 -21
- package/src/detect/build-tool.ts +14 -5
- package/src/detect/framework.ts +3 -0
- package/src/detect/package-manager.ts +1 -1
- package/src/index.ts +1 -0
- package/src/inject/gitignore.ts +13 -2
- package/src/instructions.ts +33 -7
- package/src/onboarding/apply.ts +137 -3
- package/src/onboarding/nextjs-guidance.ts +257 -0
- package/src/onboarding/nuxt-guidance.ts +129 -0
- package/src/onboarding/planner.ts +257 -6
- package/src/onboarding/session.ts +117 -27
- package/src/onboarding/target-resolution.ts +3 -3
- package/src/onboarding/umi-guidance.ts +139 -0
- package/src/types.ts +51 -3
- package/tests/apply.test.ts +319 -0
- package/tests/dev-config.test.ts +73 -0
- package/tests/doctor.test.ts +89 -0
- package/tests/init.test.ts +17 -0
- package/tests/instructions.test.ts +10 -6
- package/tests/integration-host-ide.test.ts +20 -0
- package/tests/integration-install.test.ts +65 -0
- package/tests/nextjs-guidance.test.ts +128 -0
- package/tests/nuxt-guidance.test.ts +67 -0
- package/tests/onboard.test.ts +416 -0
- package/tests/plan.test.ts +181 -21
- package/tests/runner-script.test.ts +120 -1
- package/tests/session-resolve.test.ts +116 -0
- package/tests/session.test.ts +120 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import type { OnboardingPatchPlan } from '../types.js'
|
|
4
|
+
|
|
5
|
+
type RouterMode = 'app' | 'pages' | 'mixed' | 'unknown'
|
|
6
|
+
|
|
7
|
+
export interface NextJsGuidance {
|
|
8
|
+
framework: 'react'
|
|
9
|
+
metaFramework: 'Next.js'
|
|
10
|
+
routerMode: RouterMode
|
|
11
|
+
autoApplied: string[]
|
|
12
|
+
pendingSteps: string[]
|
|
13
|
+
assistantPrompt: string
|
|
14
|
+
patches: OnboardingPatchPlan[]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const NEXT_CONFIG_CANDIDATES = ['next.config.ts', 'next.config.mjs', 'next.config.js'] as const
|
|
18
|
+
const APP_ROUTER_LAYOUTS = [
|
|
19
|
+
'app/layout.tsx',
|
|
20
|
+
'app/layout.jsx',
|
|
21
|
+
'src/app/layout.tsx',
|
|
22
|
+
'src/app/layout.jsx',
|
|
23
|
+
] as const
|
|
24
|
+
const PAGES_ROUTER_APPS = [
|
|
25
|
+
'pages/_app.tsx',
|
|
26
|
+
'pages/_app.jsx',
|
|
27
|
+
'src/pages/_app.tsx',
|
|
28
|
+
'src/pages/_app.jsx',
|
|
29
|
+
] as const
|
|
30
|
+
const PACKAGE_JSON_PATH = 'package.json'
|
|
31
|
+
|
|
32
|
+
function findFirstExisting(root: string, candidates: readonly string[]): string | undefined {
|
|
33
|
+
for (const candidate of candidates) {
|
|
34
|
+
if (fs.existsSync(path.join(root, candidate))) {
|
|
35
|
+
return candidate
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return undefined
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function detectRouterMode(root: string): RouterMode {
|
|
43
|
+
const hasAppRouter = Boolean(findFirstExisting(root, APP_ROUTER_LAYOUTS))
|
|
44
|
+
const hasPagesRouter = Boolean(findFirstExisting(root, PAGES_ROUTER_APPS))
|
|
45
|
+
|
|
46
|
+
if (hasAppRouter && hasPagesRouter) return 'mixed'
|
|
47
|
+
if (hasAppRouter) return 'app'
|
|
48
|
+
if (hasPagesRouter) return 'pages'
|
|
49
|
+
return 'unknown'
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function detectNextConfigPath(root: string): string | undefined {
|
|
53
|
+
return findFirstExisting(root, NEXT_CONFIG_CANDIDATES)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function readConfig(root: string, relativePath?: string): string {
|
|
57
|
+
if (!relativePath) return ''
|
|
58
|
+
const filePath = path.join(root, relativePath)
|
|
59
|
+
if (!fs.existsSync(filePath)) return ''
|
|
60
|
+
return fs.readFileSync(filePath, 'utf8')
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function detectPatchShape(source: string): {
|
|
64
|
+
status: OnboardingPatchPlan['status']
|
|
65
|
+
reason: OnboardingPatchPlan['reason']
|
|
66
|
+
confidence: OnboardingPatchPlan['confidence']
|
|
67
|
+
} {
|
|
68
|
+
if (
|
|
69
|
+
/export\s+default\s*\{[\s\S]*\}/m.test(source) ||
|
|
70
|
+
/module\.exports\s*=\s*\{[\s\S]*\}/m.test(source) ||
|
|
71
|
+
/const\s+[A-Za-z0-9_$]+\s*(?::[^=]+)?=\s*\{[\s\S]*\}\s*;?\s*export\s+default\s+[A-Za-z0-9_$]+\s*;?/m.test(
|
|
72
|
+
source,
|
|
73
|
+
) ||
|
|
74
|
+
/\/\*\*[\s\S]*?@type\s*\{import\('next'\)\.NextConfig\}[\s\S]*?\*\/[\s\S]*?(export\s+default|module\.exports)\s*=?\s*\{[\s\S]*\}/m.test(
|
|
75
|
+
source,
|
|
76
|
+
)
|
|
77
|
+
) {
|
|
78
|
+
return {
|
|
79
|
+
status: 'planned',
|
|
80
|
+
reason: 'next_config_object_export',
|
|
81
|
+
confidence: 'high',
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (
|
|
86
|
+
/module\.exports\s*=\s*[A-Za-z0-9_$]+\s*\(/m.test(source) ||
|
|
87
|
+
/export\s+default\s+[A-Za-z0-9_$]+\s*\(/m.test(source)
|
|
88
|
+
) {
|
|
89
|
+
return {
|
|
90
|
+
status: 'manual_patch_required',
|
|
91
|
+
reason: 'next_config_wrapped_export',
|
|
92
|
+
confidence: 'medium',
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (source.trim().length === 0) {
|
|
97
|
+
return {
|
|
98
|
+
status: 'manual_patch_required',
|
|
99
|
+
reason: 'next_config_missing',
|
|
100
|
+
confidence: 'low',
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
status: 'manual_patch_required',
|
|
106
|
+
reason: 'next_config_complex_shape',
|
|
107
|
+
confidence: 'medium',
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function buildPatchSnippet(): string {
|
|
112
|
+
return [
|
|
113
|
+
"import { webpackPlugin as inspecto } from '@inspecto-dev/plugin'",
|
|
114
|
+
'',
|
|
115
|
+
'webpack(config, { dev, isServer }) {',
|
|
116
|
+
' if (dev) {',
|
|
117
|
+
' config.plugins.push(inspecto())',
|
|
118
|
+
' }',
|
|
119
|
+
' return config',
|
|
120
|
+
'}',
|
|
121
|
+
].join('\n')
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function buildAppRouterMountSnippet(): string {
|
|
125
|
+
return [
|
|
126
|
+
"import { useEffect } from 'react'",
|
|
127
|
+
'',
|
|
128
|
+
'function InspectoProvider() {',
|
|
129
|
+
' useEffect(() => {',
|
|
130
|
+
" if (process.env.NODE_ENV !== 'production') {",
|
|
131
|
+
" import('@inspecto-dev/core').then(({ mountInspector }) => {",
|
|
132
|
+
' mountInspector()',
|
|
133
|
+
' })',
|
|
134
|
+
' }',
|
|
135
|
+
' }, [])',
|
|
136
|
+
'',
|
|
137
|
+
' return null',
|
|
138
|
+
'}',
|
|
139
|
+
'',
|
|
140
|
+
'Render <InspectoProvider /> from app/layout.tsx inside the <body> tree.',
|
|
141
|
+
].join('\n')
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function buildPagesRouterMountSnippet(): string {
|
|
145
|
+
return [
|
|
146
|
+
"import { useEffect } from 'react'",
|
|
147
|
+
'',
|
|
148
|
+
'useEffect(() => {',
|
|
149
|
+
" if (process.env.NODE_ENV !== 'production') {",
|
|
150
|
+
" import('@inspecto-dev/core').then(({ mountInspector }) => {",
|
|
151
|
+
' mountInspector()',
|
|
152
|
+
' })',
|
|
153
|
+
' }',
|
|
154
|
+
'}, [])',
|
|
155
|
+
'',
|
|
156
|
+
'Add this effect to pages/_app.tsx without changing the existing app wrapper behavior.',
|
|
157
|
+
].join('\n')
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function detectWebpackDevScriptPatch(root: string): OnboardingPatchPlan | undefined {
|
|
161
|
+
const packageJsonSource = readConfig(root, PACKAGE_JSON_PATH)
|
|
162
|
+
if (!packageJsonSource) {
|
|
163
|
+
return undefined
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
const packageJson = JSON.parse(packageJsonSource) as { scripts?: Record<string, string> }
|
|
168
|
+
const devScript = packageJson.scripts?.dev
|
|
169
|
+
if (!devScript || !/\bnext\s+dev\b/.test(devScript) || /--webpack\b/.test(devScript)) {
|
|
170
|
+
return undefined
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
path: PACKAGE_JSON_PATH,
|
|
175
|
+
status: 'manual_patch_required',
|
|
176
|
+
reason: 'next_dev_script_requires_webpack',
|
|
177
|
+
confidence: 'medium',
|
|
178
|
+
snippet: '"dev": "next dev --webpack"',
|
|
179
|
+
}
|
|
180
|
+
} catch {
|
|
181
|
+
return undefined
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function buildPendingSteps(
|
|
186
|
+
routerMode: RouterMode,
|
|
187
|
+
configPath: string,
|
|
188
|
+
needsWebpackDevScript: boolean,
|
|
189
|
+
): string[] {
|
|
190
|
+
const routerHint =
|
|
191
|
+
routerMode === 'app'
|
|
192
|
+
? 'Complete the remaining client-side mount step for your App Router entry.'
|
|
193
|
+
: routerMode === 'pages'
|
|
194
|
+
? 'Complete the remaining client-side mount step for your Pages Router entry.'
|
|
195
|
+
: routerMode === 'mixed'
|
|
196
|
+
? 'Complete the remaining client-side mount step for the router entry your team actually uses in development.'
|
|
197
|
+
: 'Complete the remaining client-side mount step in the appropriate Next.js entry file.'
|
|
198
|
+
|
|
199
|
+
return [
|
|
200
|
+
`Review the generated Next.js patch plan for ${configPath}.`,
|
|
201
|
+
...(needsWebpackDevScript
|
|
202
|
+
? ['Update the Next.js dev script to use webpack mode for Inspecto validation.']
|
|
203
|
+
: []),
|
|
204
|
+
'Keep the Inspecto webpack plugin enabled for both server and client development compilers in App Router projects.',
|
|
205
|
+
routerHint,
|
|
206
|
+
]
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function createNextJsGuidance(root: string): NextJsGuidance {
|
|
210
|
+
const configPath = detectNextConfigPath(root) ?? 'next.config.js'
|
|
211
|
+
const configSource = readConfig(root, detectNextConfigPath(root))
|
|
212
|
+
const patchShape = detectPatchShape(configSource)
|
|
213
|
+
const routerMode = detectRouterMode(root)
|
|
214
|
+
const webpackDevScriptPatch = detectWebpackDevScriptPatch(root)
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
framework: 'react',
|
|
218
|
+
metaFramework: 'Next.js',
|
|
219
|
+
routerMode,
|
|
220
|
+
autoApplied: ['dependencies', 'inspecto_settings'],
|
|
221
|
+
pendingSteps: buildPendingSteps(routerMode, configPath, Boolean(webpackDevScriptPatch)),
|
|
222
|
+
assistantPrompt:
|
|
223
|
+
'Complete the remaining Inspecto onboarding for this Next.js project. Use the generated patches directly, keep existing app behavior unchanged, avoid unrelated documentation searches unless a patch is insufficient, and finish the client-side mount step safely.',
|
|
224
|
+
patches: [
|
|
225
|
+
{
|
|
226
|
+
path: configPath,
|
|
227
|
+
status: patchShape.status,
|
|
228
|
+
reason: patchShape.reason,
|
|
229
|
+
confidence: patchShape.confidence,
|
|
230
|
+
snippet: buildPatchSnippet(),
|
|
231
|
+
},
|
|
232
|
+
...(routerMode === 'app' || routerMode === 'mixed'
|
|
233
|
+
? [
|
|
234
|
+
{
|
|
235
|
+
path: findFirstExisting(root, APP_ROUTER_LAYOUTS) ?? 'app/layout.tsx',
|
|
236
|
+
status: 'manual_patch_required' as const,
|
|
237
|
+
reason: 'next_app_router_mount',
|
|
238
|
+
confidence: 'medium' as const,
|
|
239
|
+
snippet: buildAppRouterMountSnippet(),
|
|
240
|
+
},
|
|
241
|
+
]
|
|
242
|
+
: []),
|
|
243
|
+
...(routerMode === 'pages' || routerMode === 'mixed'
|
|
244
|
+
? [
|
|
245
|
+
{
|
|
246
|
+
path: findFirstExisting(root, PAGES_ROUTER_APPS) ?? 'pages/_app.tsx',
|
|
247
|
+
status: 'manual_patch_required' as const,
|
|
248
|
+
reason: 'next_pages_router_mount',
|
|
249
|
+
confidence: 'medium' as const,
|
|
250
|
+
snippet: buildPagesRouterMountSnippet(),
|
|
251
|
+
},
|
|
252
|
+
]
|
|
253
|
+
: []),
|
|
254
|
+
...(webpackDevScriptPatch ? [webpackDevScriptPatch] : []),
|
|
255
|
+
],
|
|
256
|
+
}
|
|
257
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import type { OnboardingPatchPlan } from '../types.js'
|
|
4
|
+
|
|
5
|
+
export interface NuxtGuidance {
|
|
6
|
+
framework: 'vue'
|
|
7
|
+
metaFramework: 'Nuxt'
|
|
8
|
+
autoApplied: string[]
|
|
9
|
+
pendingSteps: string[]
|
|
10
|
+
assistantPrompt: string
|
|
11
|
+
patches: OnboardingPatchPlan[]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const NUXT_CONFIG_CANDIDATES = ['nuxt.config.ts', 'nuxt.config.js'] as const
|
|
15
|
+
|
|
16
|
+
function findFirstExisting(root: string, candidates: readonly string[]): string | undefined {
|
|
17
|
+
for (const candidate of candidates) {
|
|
18
|
+
if (fs.existsSync(path.join(root, candidate))) {
|
|
19
|
+
return candidate
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return undefined
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function readConfig(root: string, relativePath?: string): string {
|
|
27
|
+
if (!relativePath) return ''
|
|
28
|
+
const filePath = path.join(root, relativePath)
|
|
29
|
+
if (!fs.existsSync(filePath)) return ''
|
|
30
|
+
return fs.readFileSync(filePath, 'utf8')
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function detectPatchShape(source: string): {
|
|
34
|
+
status: OnboardingPatchPlan['status']
|
|
35
|
+
reason: OnboardingPatchPlan['reason']
|
|
36
|
+
confidence: OnboardingPatchPlan['confidence']
|
|
37
|
+
} {
|
|
38
|
+
if (/with[A-Za-z0-9_$]*\s*\(\s*defineNuxtConfig/m.test(source)) {
|
|
39
|
+
return {
|
|
40
|
+
status: 'manual_patch_required',
|
|
41
|
+
reason: 'nuxt_config_wrapped_export',
|
|
42
|
+
confidence: 'medium',
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (/defineNuxtConfig\s*\(\s*\{[\s\S]*\}\s*\)/m.test(source)) {
|
|
47
|
+
return {
|
|
48
|
+
status: 'planned',
|
|
49
|
+
reason: 'nuxt_config_object_export',
|
|
50
|
+
confidence: 'high',
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (source.trim().length === 0) {
|
|
55
|
+
return {
|
|
56
|
+
status: 'manual_patch_required',
|
|
57
|
+
reason: 'nuxt_config_missing',
|
|
58
|
+
confidence: 'low',
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
status: 'manual_patch_required',
|
|
64
|
+
reason: 'nuxt_config_complex_shape',
|
|
65
|
+
confidence: 'medium',
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function buildNuxtConfigSnippet(): string {
|
|
70
|
+
return [
|
|
71
|
+
"import { vitePlugin as inspecto } from '@inspecto-dev/plugin'",
|
|
72
|
+
'',
|
|
73
|
+
'export default defineNuxtConfig({',
|
|
74
|
+
' vite: {',
|
|
75
|
+
' plugins: [inspecto()],',
|
|
76
|
+
' },',
|
|
77
|
+
'})',
|
|
78
|
+
].join('\n')
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function buildNuxtPluginSnippet(): string {
|
|
82
|
+
return [
|
|
83
|
+
'export default defineNuxtPlugin(() => {',
|
|
84
|
+
' if (import.meta.dev) {',
|
|
85
|
+
" import('@inspecto-dev/core').then(({ mountInspector }) => {",
|
|
86
|
+
' mountInspector()',
|
|
87
|
+
' })',
|
|
88
|
+
' }',
|
|
89
|
+
'})',
|
|
90
|
+
].join('\n')
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function createNuxtGuidance(root: string): NuxtGuidance {
|
|
94
|
+
const configPath = findFirstExisting(root, NUXT_CONFIG_CANDIDATES) ?? 'nuxt.config.ts'
|
|
95
|
+
const configSource = readConfig(root, findFirstExisting(root, NUXT_CONFIG_CANDIDATES))
|
|
96
|
+
const patchShape = detectPatchShape(configSource)
|
|
97
|
+
|
|
98
|
+
const hasSrcDir =
|
|
99
|
+
fs.existsSync(path.join(root, 'src')) && fs.statSync(path.join(root, 'src')).isDirectory()
|
|
100
|
+
const pluginPath = hasSrcDir ? 'src/plugins/inspecto.client.ts' : 'plugins/inspecto.client.ts'
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
framework: 'vue',
|
|
104
|
+
metaFramework: 'Nuxt',
|
|
105
|
+
autoApplied: ['dependencies', 'inspecto_settings'],
|
|
106
|
+
pendingSteps: [
|
|
107
|
+
`Review the generated Nuxt patch plan for ${configPath}.`,
|
|
108
|
+
`Complete the remaining Nuxt client plugin mount step in ${pluginPath}.`,
|
|
109
|
+
],
|
|
110
|
+
assistantPrompt:
|
|
111
|
+
'Complete the remaining Inspecto onboarding for this Nuxt project. Review the generated patch plan, keep existing app behavior unchanged, and finish the client plugin mount step safely.',
|
|
112
|
+
patches: [
|
|
113
|
+
{
|
|
114
|
+
path: configPath,
|
|
115
|
+
status: patchShape.status,
|
|
116
|
+
reason: patchShape.reason,
|
|
117
|
+
confidence: patchShape.confidence,
|
|
118
|
+
snippet: buildNuxtConfigSnippet(),
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
path: pluginPath,
|
|
122
|
+
status: 'manual_patch_required',
|
|
123
|
+
reason: 'nuxt_client_plugin_mount',
|
|
124
|
+
confidence: 'medium',
|
|
125
|
+
snippet: buildNuxtPluginSnippet(),
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { buildOnboardingContext } from './context.js'
|
|
2
|
+
import { getHostIdeLabel, isSupportedHostIde } from '../integrations/capabilities.js'
|
|
3
|
+
import { createNextJsGuidance } from './nextjs-guidance.js'
|
|
4
|
+
import { createNuxtGuidance } from './nuxt-guidance.js'
|
|
5
|
+
import { createUmiGuidance } from './umi-guidance.js'
|
|
2
6
|
import type {
|
|
3
7
|
CommandMessage,
|
|
4
8
|
CommandStatus,
|
|
@@ -37,11 +41,19 @@ function supportedIde(context: OnboardingContext): string | undefined {
|
|
|
37
41
|
return context.ides.find(ide => ide.supported)?.ide
|
|
38
42
|
}
|
|
39
43
|
|
|
44
|
+
function shouldInstallInspectoExtension(ide?: string): boolean {
|
|
45
|
+
return Boolean(ide && isSupportedHostIde(ide))
|
|
46
|
+
}
|
|
47
|
+
|
|
40
48
|
function supportedProvider(context: OnboardingContext): string | undefined {
|
|
41
49
|
return context.providers.find(provider => provider.supported)?.id
|
|
42
50
|
}
|
|
43
51
|
|
|
44
52
|
function buildToolBlockers(context: OnboardingContext): CommandMessage[] {
|
|
53
|
+
if (isGuidedMetaFrameworkScenario(context)) {
|
|
54
|
+
return []
|
|
55
|
+
}
|
|
56
|
+
|
|
45
57
|
if (context.buildTools.unsupported.length > 0) {
|
|
46
58
|
return [
|
|
47
59
|
message(
|
|
@@ -214,6 +226,77 @@ function manualBuildToolActions(context: OnboardingContext): PlanResult['actions
|
|
|
214
226
|
]
|
|
215
227
|
}
|
|
216
228
|
|
|
229
|
+
function hasUnsupportedBuildTool(context: OnboardingContext, buildTool: string): boolean {
|
|
230
|
+
return context.buildTools.unsupported.includes(buildTool)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function isGuidedNextJsScenario(context: OnboardingContext): boolean {
|
|
234
|
+
return (
|
|
235
|
+
context.buildTools.supported.length === 0 &&
|
|
236
|
+
hasUnsupportedBuildTool(context, 'Next.js') &&
|
|
237
|
+
context.frameworks.supported.includes('react')
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function isGuidedNuxtScenario(context: OnboardingContext): boolean {
|
|
242
|
+
return (
|
|
243
|
+
context.buildTools.supported.length === 0 &&
|
|
244
|
+
hasUnsupportedBuildTool(context, 'Nuxt') &&
|
|
245
|
+
context.frameworks.supported.includes('vue')
|
|
246
|
+
)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function isGuidedUmiScenario(context: OnboardingContext): boolean {
|
|
250
|
+
return hasUnsupportedBuildTool(context, 'Umi') && context.frameworks.supported.includes('react')
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function isGuidedMetaFrameworkScenario(context: OnboardingContext): boolean {
|
|
254
|
+
return (
|
|
255
|
+
isGuidedNextJsScenario(context) || isGuidedNuxtScenario(context) || isGuidedUmiScenario(context)
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function guidedBuildToolWarnings(
|
|
260
|
+
context: OnboardingContext,
|
|
261
|
+
guidedBuildTool: string,
|
|
262
|
+
): CommandMessage[] {
|
|
263
|
+
return context.buildTools.unsupported
|
|
264
|
+
.filter(buildTool => buildTool !== guidedBuildTool)
|
|
265
|
+
.map(buildTool =>
|
|
266
|
+
message(
|
|
267
|
+
'additional-unsupported-build-tool',
|
|
268
|
+
`Additional unsupported build tool also detected: ${buildTool}`,
|
|
269
|
+
),
|
|
270
|
+
)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function guidedFrameworkWarnings(context: OnboardingContext, framework: string): CommandMessage[] {
|
|
274
|
+
return context.frameworks.unsupported
|
|
275
|
+
.filter(item => item !== framework)
|
|
276
|
+
.map(item =>
|
|
277
|
+
message(
|
|
278
|
+
'additional-unsupported-framework',
|
|
279
|
+
`Additional unsupported framework also detected: ${item}`,
|
|
280
|
+
),
|
|
281
|
+
)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function buildGuidedWarnings(
|
|
285
|
+
context: OnboardingContext,
|
|
286
|
+
guidedBuildTool: string,
|
|
287
|
+
guidedFramework: string,
|
|
288
|
+
): CommandMessage[] {
|
|
289
|
+
return uniqueMessages([
|
|
290
|
+
...guidedBuildToolWarnings(context, guidedBuildTool),
|
|
291
|
+
...guidedFrameworkWarnings(context, guidedFramework),
|
|
292
|
+
...unsupportedEnvironmentWarnings(context),
|
|
293
|
+
]).filter(
|
|
294
|
+
warning =>
|
|
295
|
+
warning.message !==
|
|
296
|
+
`Unsupported framework(s) also detected: ${context.frameworks.unsupported.join(', ')}`,
|
|
297
|
+
)
|
|
298
|
+
}
|
|
299
|
+
|
|
217
300
|
function manualFrameworkActions(context: OnboardingContext): PlanResult['actions'] {
|
|
218
301
|
if (context.frameworks.unsupported.length > 0) {
|
|
219
302
|
return [
|
|
@@ -267,6 +350,170 @@ export async function createDetectionResult(root: string): Promise<DetectionResu
|
|
|
267
350
|
}
|
|
268
351
|
|
|
269
352
|
export function createPlanResult(context: OnboardingContext): PlanResult {
|
|
353
|
+
if (isGuidedNextJsScenario(context)) {
|
|
354
|
+
const ide = supportedIde(context)
|
|
355
|
+
const provider = supportedProvider(context)
|
|
356
|
+
const guidance = createNextJsGuidance(context.root)
|
|
357
|
+
const actions: PlanResult['actions'] = [
|
|
358
|
+
{
|
|
359
|
+
type: 'install_dependency',
|
|
360
|
+
target: '@inspecto-dev/plugin @inspecto-dev/core',
|
|
361
|
+
description: `Install the Inspecto runtime packages with ${context.packageManager}.`,
|
|
362
|
+
},
|
|
363
|
+
]
|
|
364
|
+
|
|
365
|
+
if (shouldInstallInspectoExtension(ide) && ide) {
|
|
366
|
+
actions.push({
|
|
367
|
+
type: 'install_extension',
|
|
368
|
+
target: ide,
|
|
369
|
+
description: `Install the Inspecto ${getHostIdeLabel(ide as Parameters<typeof getHostIdeLabel>[0])} extension.`,
|
|
370
|
+
})
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
actions.push(
|
|
374
|
+
{
|
|
375
|
+
type: 'generate_patch_plan',
|
|
376
|
+
target: 'next.config',
|
|
377
|
+
description: 'Generate a guided patch plan for the Next.js Inspecto webpack integration.',
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
type: 'manual_confirmation',
|
|
381
|
+
target: context.root,
|
|
382
|
+
description:
|
|
383
|
+
'Complete the remaining client-side Inspecto mount step in your assistant or editor.',
|
|
384
|
+
},
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
const defaults: PlanResult['defaults'] = {
|
|
388
|
+
shared: false,
|
|
389
|
+
extension: shouldInstallInspectoExtension(ide),
|
|
390
|
+
...(provider ? { provider } : {}),
|
|
391
|
+
...(ide ? { ide } : {}),
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return {
|
|
395
|
+
status: 'warning',
|
|
396
|
+
warnings: buildGuidedWarnings(context, 'Next.js', 'react'),
|
|
397
|
+
blockers: [],
|
|
398
|
+
strategy: 'guided',
|
|
399
|
+
actions,
|
|
400
|
+
defaults,
|
|
401
|
+
framework: guidance.framework,
|
|
402
|
+
metaFramework: guidance.metaFramework,
|
|
403
|
+
routerMode: guidance.routerMode,
|
|
404
|
+
autoApplied: guidance.autoApplied,
|
|
405
|
+
pendingSteps: guidance.pendingSteps,
|
|
406
|
+
assistantPrompt: guidance.assistantPrompt,
|
|
407
|
+
patches: guidance.patches,
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (isGuidedNuxtScenario(context)) {
|
|
412
|
+
const ide = supportedIde(context)
|
|
413
|
+
const provider = supportedProvider(context)
|
|
414
|
+
const guidance = createNuxtGuidance(context.root)
|
|
415
|
+
const actions: PlanResult['actions'] = [
|
|
416
|
+
{
|
|
417
|
+
type: 'install_dependency',
|
|
418
|
+
target: '@inspecto-dev/plugin @inspecto-dev/core',
|
|
419
|
+
description: `Install the Inspecto runtime packages with ${context.packageManager}.`,
|
|
420
|
+
},
|
|
421
|
+
]
|
|
422
|
+
|
|
423
|
+
if (shouldInstallInspectoExtension(ide) && ide) {
|
|
424
|
+
actions.push({
|
|
425
|
+
type: 'install_extension',
|
|
426
|
+
target: ide,
|
|
427
|
+
description: `Install the Inspecto ${getHostIdeLabel(ide as Parameters<typeof getHostIdeLabel>[0])} extension.`,
|
|
428
|
+
})
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
actions.push(
|
|
432
|
+
{
|
|
433
|
+
type: 'generate_patch_plan',
|
|
434
|
+
target: 'nuxt.config',
|
|
435
|
+
description: 'Generate a guided patch plan for the Nuxt Inspecto Vite integration.',
|
|
436
|
+
},
|
|
437
|
+
{
|
|
438
|
+
type: 'manual_confirmation',
|
|
439
|
+
target: context.root,
|
|
440
|
+
description:
|
|
441
|
+
'Complete the remaining Nuxt client plugin mount step in your assistant or editor.',
|
|
442
|
+
},
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
const defaults: PlanResult['defaults'] = {
|
|
446
|
+
shared: false,
|
|
447
|
+
extension: shouldInstallInspectoExtension(ide),
|
|
448
|
+
...(provider ? { provider } : {}),
|
|
449
|
+
...(ide ? { ide } : {}),
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
return {
|
|
453
|
+
status: 'warning',
|
|
454
|
+
warnings: buildGuidedWarnings(context, 'Nuxt', 'vue'),
|
|
455
|
+
blockers: [],
|
|
456
|
+
strategy: 'guided',
|
|
457
|
+
actions,
|
|
458
|
+
defaults,
|
|
459
|
+
framework: guidance.framework,
|
|
460
|
+
metaFramework: guidance.metaFramework,
|
|
461
|
+
autoApplied: guidance.autoApplied,
|
|
462
|
+
pendingSteps: guidance.pendingSteps,
|
|
463
|
+
assistantPrompt: guidance.assistantPrompt,
|
|
464
|
+
patches: guidance.patches,
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (isGuidedUmiScenario(context)) {
|
|
469
|
+
const ide = supportedIde(context)
|
|
470
|
+
const provider = supportedProvider(context)
|
|
471
|
+
const guidance = createUmiGuidance(context.root)
|
|
472
|
+
const actions: PlanResult['actions'] = [
|
|
473
|
+
{
|
|
474
|
+
type: 'install_dependency',
|
|
475
|
+
target: '@inspecto-dev/plugin @inspecto-dev/core',
|
|
476
|
+
description: `Install the Inspecto runtime packages with ${context.packageManager}.`,
|
|
477
|
+
},
|
|
478
|
+
]
|
|
479
|
+
|
|
480
|
+
if (shouldInstallInspectoExtension(ide) && ide) {
|
|
481
|
+
actions.push({
|
|
482
|
+
type: 'install_extension',
|
|
483
|
+
target: ide,
|
|
484
|
+
description: `Install the Inspecto ${getHostIdeLabel(ide as Parameters<typeof getHostIdeLabel>[0])} extension.`,
|
|
485
|
+
})
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
actions.push({
|
|
489
|
+
type: 'generate_patch_plan',
|
|
490
|
+
target: 'umi.config',
|
|
491
|
+
description: 'Generate a guided patch plan for the Umi Inspecto webpack integration.',
|
|
492
|
+
})
|
|
493
|
+
|
|
494
|
+
const defaults: PlanResult['defaults'] = {
|
|
495
|
+
shared: false,
|
|
496
|
+
extension: shouldInstallInspectoExtension(ide),
|
|
497
|
+
...(provider ? { provider } : {}),
|
|
498
|
+
...(ide ? { ide } : {}),
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
return {
|
|
502
|
+
status: 'warning',
|
|
503
|
+
warnings: buildGuidedWarnings(context, 'Umi', 'react'),
|
|
504
|
+
blockers: [],
|
|
505
|
+
strategy: 'guided',
|
|
506
|
+
actions,
|
|
507
|
+
defaults,
|
|
508
|
+
framework: guidance.framework,
|
|
509
|
+
metaFramework: guidance.metaFramework,
|
|
510
|
+
autoApplied: guidance.autoApplied,
|
|
511
|
+
pendingSteps: guidance.pendingSteps,
|
|
512
|
+
assistantPrompt: guidance.assistantPrompt,
|
|
513
|
+
patches: guidance.patches,
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
270
517
|
const warnings = uniqueMessages([
|
|
271
518
|
...unsupportedEnvironmentWarnings(context),
|
|
272
519
|
...buildToolWarnings(context),
|
|
@@ -319,18 +566,19 @@ export function createPlanResult(context: OnboardingContext): PlanResult {
|
|
|
319
566
|
}
|
|
320
567
|
|
|
321
568
|
const ide = supportedIde(context)
|
|
322
|
-
if (ide
|
|
569
|
+
if (shouldInstallInspectoExtension(ide) && ide) {
|
|
323
570
|
actions.push({
|
|
324
571
|
type: 'install_extension',
|
|
325
|
-
target:
|
|
326
|
-
description:
|
|
572
|
+
target: ide,
|
|
573
|
+
description: `Install the Inspecto ${getHostIdeLabel(ide as Parameters<typeof getHostIdeLabel>[0])} extension.`,
|
|
327
574
|
})
|
|
328
575
|
}
|
|
329
576
|
}
|
|
330
577
|
|
|
578
|
+
const ide = supportedIde(context)
|
|
331
579
|
const defaults: PlanResult['defaults'] = {
|
|
332
580
|
shared: false,
|
|
333
|
-
extension:
|
|
581
|
+
extension: shouldInstallInspectoExtension(ide),
|
|
334
582
|
}
|
|
335
583
|
|
|
336
584
|
const provider = supportedProvider(context)
|
|
@@ -338,7 +586,6 @@ export function createPlanResult(context: OnboardingContext): PlanResult {
|
|
|
338
586
|
defaults.provider = provider
|
|
339
587
|
}
|
|
340
588
|
|
|
341
|
-
const ide = supportedIde(context)
|
|
342
589
|
if (ide) {
|
|
343
590
|
defaults.ide = ide
|
|
344
591
|
}
|
|
@@ -355,6 +602,10 @@ export function createPlanResult(context: OnboardingContext): PlanResult {
|
|
|
355
602
|
|
|
356
603
|
export function planManualFollowUp(result: PlanResult): string[] {
|
|
357
604
|
return result.actions
|
|
358
|
-
.filter(action =>
|
|
605
|
+
.filter(action =>
|
|
606
|
+
['manual_step', 'generate_patch_plan', 'generate_file', 'manual_confirmation'].includes(
|
|
607
|
+
action.type,
|
|
608
|
+
),
|
|
609
|
+
)
|
|
359
610
|
.map(action => action.description)
|
|
360
611
|
}
|