@inspecto-dev/cli 0.3.3 → 0.3.4
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 +6 -6
- package/.turbo/turbo-test.log +5450 -3335
- package/CHANGELOG.md +20 -0
- package/dist/bin.js +5 -2
- package/dist/{chunk-LJOKPCPD.js → chunk-2MOEVONN.js} +481 -49
- package/dist/index.d.ts +7 -0
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/src/bin.ts +4 -1
- package/src/commands/doctor.ts +27 -0
- package/src/commands/integration-install.ts +99 -4
- package/src/commands/onboard.ts +14 -0
- package/src/detect/build-tool.ts +198 -10
- package/src/onboarding/apply.ts +120 -27
- package/src/onboarding/planner.ts +80 -4
- package/src/onboarding/session.ts +11 -5
- package/src/onboarding/target-resolution.ts +78 -2
- package/src/types.ts +7 -0
- package/tests/apply.test.ts +234 -0
- package/tests/build-tool.test.ts +199 -0
- package/tests/doctor.test.ts +41 -0
- package/tests/install-wrapper.test.ts +56 -0
- package/tests/integration-install.test.ts +128 -0
- package/tests/onboard.test.ts +95 -0
- package/tests/plan.test.ts +102 -0
package/dist/index.d.ts
CHANGED
|
@@ -49,9 +49,14 @@ interface OnboardingContext {
|
|
|
49
49
|
providers: OnboardingProvider[];
|
|
50
50
|
}
|
|
51
51
|
interface OnboardingTargetCandidate {
|
|
52
|
+
id?: string;
|
|
53
|
+
candidateId?: string;
|
|
52
54
|
packagePath: string;
|
|
53
55
|
configPath: string;
|
|
56
|
+
label?: string;
|
|
54
57
|
buildTool: BuildTool;
|
|
58
|
+
isLegacyRspack?: boolean;
|
|
59
|
+
isLegacyWebpack?: boolean;
|
|
55
60
|
frameworks: string[];
|
|
56
61
|
automaticInjection: boolean;
|
|
57
62
|
}
|
|
@@ -60,6 +65,8 @@ interface OnboardingTargetResolution {
|
|
|
60
65
|
selected?: OnboardingTargetCandidate;
|
|
61
66
|
candidates: OnboardingTargetCandidate[];
|
|
62
67
|
reason: string;
|
|
68
|
+
selectionPurpose?: string;
|
|
69
|
+
selectionInstructions?: string;
|
|
63
70
|
}
|
|
64
71
|
interface OnboardingSummary {
|
|
65
72
|
headline: string;
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inspecto-dev/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"description": "CLI tools for Inspecto onboarding and lifecycle management",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"inspecto",
|
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
"ora": "^9.3.0",
|
|
21
21
|
"picocolors": "^1.0.0",
|
|
22
22
|
"prompts": "^2.4.2",
|
|
23
|
-
"@inspecto-dev/types": "0.3.
|
|
23
|
+
"@inspecto-dev/types": "0.3.4"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@types/node": "^20.
|
|
26
|
+
"@types/node": "^20.19.39",
|
|
27
27
|
"@types/prompts": "^2.4.9",
|
|
28
28
|
"tsup": "^8.0.2",
|
|
29
29
|
"typescript": "^5.4.5",
|
package/src/bin.ts
CHANGED
|
@@ -128,7 +128,10 @@ export function createCli(_argv: readonly string[] = process.argv): CAC {
|
|
|
128
128
|
cli
|
|
129
129
|
.command('onboard', 'Run assistant-oriented Inspecto onboarding in one structured flow')
|
|
130
130
|
.option('--json', 'Print machine-readable JSON output', { default: false })
|
|
131
|
-
.option(
|
|
131
|
+
.option(
|
|
132
|
+
'--target <candidateIdOrPath>',
|
|
133
|
+
'Select the build target to onboard using a returned candidateId or compatible config path',
|
|
134
|
+
)
|
|
132
135
|
.option('--yes', 'Accept a lightweight confirmation gate automatically', { default: false })
|
|
133
136
|
.option('--shared', 'Write shared Inspecto settings instead of local-only settings')
|
|
134
137
|
.option('--skip-install', 'Skip npm dependency installation')
|
package/src/commands/doctor.ts
CHANGED
|
@@ -282,6 +282,33 @@ export async function collectDoctorResult(root = process.cwd()): Promise<DoctorR
|
|
|
282
282
|
const settings = await readJSON(targetPath)
|
|
283
283
|
if (settings) {
|
|
284
284
|
checks.push(createDiagnostic('settings-valid', 'ok', `.inspecto/${fileName} valid`))
|
|
285
|
+
|
|
286
|
+
const configuredIde =
|
|
287
|
+
typeof (settings as Record<string, unknown>).ide === 'string'
|
|
288
|
+
? ((settings as Record<string, unknown>).ide as string)
|
|
289
|
+
: undefined
|
|
290
|
+
const detectedIdeCandidates = ideProbe.detected.map(item => item.ide)
|
|
291
|
+
if (
|
|
292
|
+
configuredIde &&
|
|
293
|
+
detectedIdeCandidates.length > 0 &&
|
|
294
|
+
!detectedIdeCandidates.includes(configuredIde)
|
|
295
|
+
) {
|
|
296
|
+
checks.push(
|
|
297
|
+
createDiagnostic(
|
|
298
|
+
'settings-ide-mismatch',
|
|
299
|
+
'warning',
|
|
300
|
+
`.inspecto/${fileName} sets ide=${configuredIde}, but the current environment looks like ${detectedIdeCandidates.join(', ')}. Inspecto will use the configured IDE from ${fileName}.`,
|
|
301
|
+
[
|
|
302
|
+
`Update .inspecto/${fileName} if you want Inspecto to target the currently detected IDE instead.`,
|
|
303
|
+
],
|
|
304
|
+
{
|
|
305
|
+
configuredIde,
|
|
306
|
+
detectedIdeCandidates,
|
|
307
|
+
precedence: `configured ide from ${fileName}`,
|
|
308
|
+
},
|
|
309
|
+
),
|
|
310
|
+
)
|
|
311
|
+
}
|
|
285
312
|
} else {
|
|
286
313
|
checks.push(
|
|
287
314
|
createDiagnostic(
|
|
@@ -2,10 +2,19 @@ import fs from 'node:fs/promises'
|
|
|
2
2
|
import { homedir } from 'node:os'
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
import { fileURLToPath } from 'node:url'
|
|
5
|
-
import { exists, writeFile } from '../utils/fs.js'
|
|
5
|
+
import { exists, readJSON, writeFile, writeJSON } from '../utils/fs.js'
|
|
6
6
|
import { log } from '../utils/logger.js'
|
|
7
7
|
import { writeCommandOutput } from '../utils/output.js'
|
|
8
8
|
import { runIntegrationAutomation } from './integration-automation.js'
|
|
9
|
+
import { resolveIntegrationDispatchMode } from './integration-dispatch-mode.js'
|
|
10
|
+
import { resolveIntegrationHostIde } from './integration-host-ide.js'
|
|
11
|
+
import { isSupportedHostIde, type SupportedHostIde } from '../integrations/capabilities.js'
|
|
12
|
+
import {
|
|
13
|
+
DEFAULT_PROVIDER_MODE,
|
|
14
|
+
VALID_MODES,
|
|
15
|
+
type Provider,
|
|
16
|
+
type ProviderMode,
|
|
17
|
+
} from '@inspecto-dev/types'
|
|
9
18
|
|
|
10
19
|
const REPO_RAW_BASE = 'https://raw.githubusercontent.com/inspecto-dev/inspecto/main'
|
|
11
20
|
const TOTAL_STEPS = 6
|
|
@@ -55,6 +64,12 @@ interface InstallPlan {
|
|
|
55
64
|
nextStep: string
|
|
56
65
|
}
|
|
57
66
|
|
|
67
|
+
interface InspectoSettingsShape {
|
|
68
|
+
ide?: string
|
|
69
|
+
'provider.default'?: string
|
|
70
|
+
[key: string]: unknown
|
|
71
|
+
}
|
|
72
|
+
|
|
58
73
|
export interface IntegrationInstallResult {
|
|
59
74
|
status: 'launched' | 'partial' | 'blocked' | 'preview' | 'preview_blocked'
|
|
60
75
|
assistant: string
|
|
@@ -117,7 +132,7 @@ const INTEGRATION_MANIFESTS: IntegrationManifest[] = [
|
|
|
117
132
|
{
|
|
118
133
|
assistant: 'coco',
|
|
119
134
|
type: 'native-skill',
|
|
120
|
-
installTarget: '.
|
|
135
|
+
installTarget: '.trae/skills/inspecto-onboarding/',
|
|
121
136
|
preferredInstall:
|
|
122
137
|
'npx @inspecto-dev/cli integrations install coco --host-ide <vscode|cursor|trae|trae-cn>',
|
|
123
138
|
cliSupported: true,
|
|
@@ -194,6 +209,10 @@ export async function installIntegration(
|
|
|
194
209
|
}
|
|
195
210
|
}
|
|
196
211
|
|
|
212
|
+
if (shouldPersistProjectOnboardingDefaults(options)) {
|
|
213
|
+
await persistProjectOnboardingDefaults(assistant as AssistantId, options)
|
|
214
|
+
}
|
|
215
|
+
|
|
197
216
|
const stepOneMessage = options.preview
|
|
198
217
|
? formatIntegrationStep(1, `Previewing ${getAssistantLabel(assistant)} integration assets`)
|
|
199
218
|
: formatIntegrationStep(1, `Installed ${getAssistantLabel(assistant)} integration assets`)
|
|
@@ -292,6 +311,70 @@ export async function installIntegration(
|
|
|
292
311
|
return result
|
|
293
312
|
}
|
|
294
313
|
|
|
314
|
+
function shouldPersistProjectOnboardingDefaults(
|
|
315
|
+
options: InstallIntegrationOptions,
|
|
316
|
+
): options is InstallIntegrationOptions & { ide: SupportedHostIde } {
|
|
317
|
+
return (
|
|
318
|
+
!options.preview && !shouldSkipAutomationForInstall(options) && isSupportedHostIde(options.ide)
|
|
319
|
+
)
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function isProviderAssistant(value: string): value is Provider {
|
|
323
|
+
return Object.prototype.hasOwnProperty.call(VALID_MODES, value)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
async function resolveProviderDefaultForAssistant(
|
|
327
|
+
assistant: AssistantId,
|
|
328
|
+
ide: SupportedHostIde,
|
|
329
|
+
): Promise<string | undefined> {
|
|
330
|
+
if (!isProviderAssistant(assistant)) {
|
|
331
|
+
return undefined
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
let mode: ProviderMode | undefined
|
|
335
|
+
if (assistant === 'codex' || assistant === 'claude-code' || assistant === 'gemini') {
|
|
336
|
+
const dispatchMode = await resolveIntegrationDispatchMode({ assistant, hostIde: ide })
|
|
337
|
+
mode = dispatchMode.mode ?? DEFAULT_PROVIDER_MODE[assistant]
|
|
338
|
+
} else {
|
|
339
|
+
mode = DEFAULT_PROVIDER_MODE[assistant]
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (!mode || !VALID_MODES[assistant].includes(mode)) {
|
|
343
|
+
return undefined
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return `${assistant}.${mode}`
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
async function persistProjectOnboardingDefaults(
|
|
350
|
+
assistant: AssistantId,
|
|
351
|
+
options: InstallIntegrationOptions & { ide: SupportedHostIde },
|
|
352
|
+
): Promise<void> {
|
|
353
|
+
const settingsPath = path.join(process.cwd(), '.inspecto', 'settings.local.json')
|
|
354
|
+
const existingSettings = await readJSON<InspectoSettingsShape>(settingsPath)
|
|
355
|
+
const resolvedHostIde = await resolveIntegrationHostIde({
|
|
356
|
+
explicitIde: options.ide,
|
|
357
|
+
cwd: process.cwd(),
|
|
358
|
+
})
|
|
359
|
+
const providerDefault =
|
|
360
|
+
resolvedHostIde.ide && resolvedHostIde.confidence !== 'low'
|
|
361
|
+
? await resolveProviderDefaultForAssistant(assistant, resolvedHostIde.ide)
|
|
362
|
+
: undefined
|
|
363
|
+
const mergedSettings =
|
|
364
|
+
existingSettings && typeof existingSettings === 'object'
|
|
365
|
+
? {
|
|
366
|
+
...existingSettings,
|
|
367
|
+
ide: options.ide,
|
|
368
|
+
...(providerDefault ? { 'provider.default': providerDefault } : {}),
|
|
369
|
+
}
|
|
370
|
+
: {
|
|
371
|
+
ide: options.ide,
|
|
372
|
+
...(providerDefault ? { 'provider.default': providerDefault } : {}),
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
await writeJSON(settingsPath, mergedSettings)
|
|
376
|
+
}
|
|
377
|
+
|
|
295
378
|
function shouldSkipAutomationForInstall(options: InstallIntegrationOptions): boolean {
|
|
296
379
|
return options.scope === 'user' && !options.preview
|
|
297
380
|
}
|
|
@@ -375,6 +458,12 @@ function resolveInstallPlan(assistant: string, options: InstallIntegrationOption
|
|
|
375
458
|
target: '.trae/skills/inspecto-onboarding/SKILL.md',
|
|
376
459
|
localSource: 'skills/inspecto-onboarding-trae/SKILL.md',
|
|
377
460
|
},
|
|
461
|
+
{
|
|
462
|
+
source: `${REPO_RAW_BASE}/skills/inspecto-onboarding-trae/scripts/run-inspecto.sh`,
|
|
463
|
+
target: '.trae/skills/inspecto-onboarding/scripts/run-inspecto.sh',
|
|
464
|
+
localSource: 'skills/inspecto-onboarding-trae/scripts/run-inspecto.sh',
|
|
465
|
+
executable: true,
|
|
466
|
+
},
|
|
378
467
|
],
|
|
379
468
|
successMessage: 'Installed Trae skill to .trae/skills/inspecto-onboarding/SKILL.md',
|
|
380
469
|
nextStep: 'Open a new Trae chat and verify the inspecto-onboarding skill is available.',
|
|
@@ -384,11 +473,17 @@ function resolveInstallPlan(assistant: string, options: InstallIntegrationOption
|
|
|
384
473
|
assets: [
|
|
385
474
|
{
|
|
386
475
|
source: `${REPO_RAW_BASE}/skills/inspecto-onboarding-trae/SKILL.md`,
|
|
387
|
-
target: '.
|
|
476
|
+
target: '.trae/skills/inspecto-onboarding/SKILL.md',
|
|
388
477
|
localSource: 'skills/inspecto-onboarding-trae/SKILL.md',
|
|
389
478
|
},
|
|
479
|
+
{
|
|
480
|
+
source: `${REPO_RAW_BASE}/skills/inspecto-onboarding-trae/scripts/run-inspecto.sh`,
|
|
481
|
+
target: '.trae/skills/inspecto-onboarding/scripts/run-inspecto.sh',
|
|
482
|
+
localSource: 'skills/inspecto-onboarding-trae/scripts/run-inspecto.sh',
|
|
483
|
+
executable: true,
|
|
484
|
+
},
|
|
390
485
|
],
|
|
391
|
-
successMessage: 'Installed Coco skill to .
|
|
486
|
+
successMessage: 'Installed Coco skill to .trae/skills/inspecto-onboarding/SKILL.md',
|
|
392
487
|
nextStep: 'Start a new Coco session.',
|
|
393
488
|
}
|
|
394
489
|
default:
|
package/src/commands/onboard.ts
CHANGED
|
@@ -39,6 +39,20 @@ function printOnboardResult(result: OnboardCommandResult): void {
|
|
|
39
39
|
log.info(`Status: ${result.status}`)
|
|
40
40
|
log.info(result.summary.headline)
|
|
41
41
|
|
|
42
|
+
if (result.status === 'needs_target_selection') {
|
|
43
|
+
if (result.target.selectionPurpose) {
|
|
44
|
+
log.warn(result.target.selectionPurpose)
|
|
45
|
+
}
|
|
46
|
+
for (const candidate of result.target.candidates) {
|
|
47
|
+
const identifier = candidate.candidateId ?? candidate.id ?? candidate.configPath
|
|
48
|
+
const label = candidate.label ?? candidate.configPath
|
|
49
|
+
log.hint(`${identifier}: ${label}`)
|
|
50
|
+
}
|
|
51
|
+
if (result.target.selectionInstructions) {
|
|
52
|
+
log.hint(result.target.selectionInstructions)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
42
56
|
for (const change of result.summary.changes) {
|
|
43
57
|
log.hint(change)
|
|
44
58
|
}
|
package/src/detect/build-tool.ts
CHANGED
|
@@ -50,6 +50,37 @@ async function getResolvedPackageVersion(pkgName: string, root: string): Promise
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
function parseFirstSemver(
|
|
54
|
+
version: string | null,
|
|
55
|
+
): { major: number; minor: number; patch: number } | null {
|
|
56
|
+
if (!version) return null
|
|
57
|
+
|
|
58
|
+
const match = version.match(/(\d+)\.(\d+)\.(\d+)/)
|
|
59
|
+
if (!match) {
|
|
60
|
+
return null
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
major: Number(match[1]),
|
|
65
|
+
minor: Number(match[2]),
|
|
66
|
+
patch: Number(match[3]),
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function isLegacyRspackVersion(version: string | null): boolean {
|
|
71
|
+
const parsed = parseFirstSemver(version)
|
|
72
|
+
if (!parsed) return false
|
|
73
|
+
|
|
74
|
+
return parsed.major === 0 && parsed.minor < 4
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function isLegacyWebpackVersion(version: string | null): boolean {
|
|
78
|
+
const parsed = parseFirstSemver(version)
|
|
79
|
+
if (!parsed) return false
|
|
80
|
+
|
|
81
|
+
return parsed.major === 4
|
|
82
|
+
}
|
|
83
|
+
|
|
53
84
|
/** Supported build tools in v1 */
|
|
54
85
|
const SUPPORTED_PATTERNS: { tool: BuildTool; files: string[]; label: string }[] = [
|
|
55
86
|
{
|
|
@@ -76,7 +107,22 @@ const SUPPORTED_PATTERNS: { tool: BuildTool; files: string[]; label: string }[]
|
|
|
76
107
|
},
|
|
77
108
|
{
|
|
78
109
|
tool: 'webpack',
|
|
79
|
-
files: [
|
|
110
|
+
files: [
|
|
111
|
+
'webpack.config.js',
|
|
112
|
+
'webpack.config.ts',
|
|
113
|
+
'webpack.config.mjs',
|
|
114
|
+
'webpack.config.cjs',
|
|
115
|
+
'webpack.config.common.js',
|
|
116
|
+
'webpack.config.common.ts',
|
|
117
|
+
'webpack.config.dev.js',
|
|
118
|
+
'webpack.config.dev.ts',
|
|
119
|
+
'webpack.config.prod.js',
|
|
120
|
+
'webpack.config.prod.ts',
|
|
121
|
+
'webpack.config.esbuild.js',
|
|
122
|
+
'webpack.config.esbuild.ts',
|
|
123
|
+
'webpack.config.build-pre.js',
|
|
124
|
+
'webpack.config.build-pre.ts',
|
|
125
|
+
],
|
|
80
126
|
label: 'Webpack',
|
|
81
127
|
},
|
|
82
128
|
{
|
|
@@ -280,6 +326,119 @@ interface PatternContext {
|
|
|
280
326
|
scripts: Record<string, string>
|
|
281
327
|
}
|
|
282
328
|
|
|
329
|
+
function rankScriptCommand(name: string, command: string): number {
|
|
330
|
+
const haystack = `${name} ${command}`.toLowerCase()
|
|
331
|
+
let score = 0
|
|
332
|
+
|
|
333
|
+
if (/(^|[\s:_-])(start|dev|serve|watch)([\s:_-]|$)/.test(haystack)) score += 8
|
|
334
|
+
if (/(^|[\s:_-])(prod|build|release|stats)([\s:_-]|$)/.test(haystack)) score -= 3
|
|
335
|
+
if (/(^|[\s:_-])(dll|vendor)([\s:_-]|$)/.test(haystack)) score -= 6
|
|
336
|
+
if (haystack.includes('webpack-dev-server')) score += 3
|
|
337
|
+
if (haystack.includes('webpack')) score += 1
|
|
338
|
+
if (haystack.includes('rspack')) score += 1
|
|
339
|
+
|
|
340
|
+
return score
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function extractConfigArgs(scriptContent: string): string[] {
|
|
344
|
+
return Array.from(scriptContent.matchAll(/(?:-c|--config)\s+([^\s'"`;]+)/g))
|
|
345
|
+
.map(match => match[1])
|
|
346
|
+
.filter((value): value is string => Boolean(value))
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
async function resolveScriptRelativeCandidate(
|
|
350
|
+
targetRoot: string,
|
|
351
|
+
scriptPath: string,
|
|
352
|
+
candidate: string,
|
|
353
|
+
): Promise<string | null> {
|
|
354
|
+
const normalizedCandidate = candidate.replace(/^['"`]|['"`]$/g, '')
|
|
355
|
+
const normalizedRelativeCandidate = path.normalize(normalizedCandidate)
|
|
356
|
+
const possiblePaths: string[] = []
|
|
357
|
+
|
|
358
|
+
if (normalizedRelativeCandidate.startsWith('..')) {
|
|
359
|
+
possiblePaths.push(
|
|
360
|
+
path.normalize(path.join(path.dirname(scriptPath), normalizedRelativeCandidate)),
|
|
361
|
+
)
|
|
362
|
+
} else {
|
|
363
|
+
possiblePaths.push(normalizedRelativeCandidate)
|
|
364
|
+
possiblePaths.push(
|
|
365
|
+
path.normalize(path.join(path.dirname(scriptPath), normalizedRelativeCandidate)),
|
|
366
|
+
)
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
for (const possiblePath of possiblePaths) {
|
|
370
|
+
if (await exists(path.join(targetRoot, possiblePath))) {
|
|
371
|
+
return possiblePath.split(path.sep).join('/')
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return null
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
async function resolveRspackConfigFromScript(
|
|
379
|
+
targetRoot: string,
|
|
380
|
+
scriptPath: string,
|
|
381
|
+
): Promise<string | null> {
|
|
382
|
+
const scriptContent = await readFile(path.join(targetRoot, scriptPath))
|
|
383
|
+
if (!scriptContent) {
|
|
384
|
+
return null
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
for (const candidate of extractConfigArgs(scriptContent)) {
|
|
388
|
+
const resolved = await resolveScriptRelativeCandidate(targetRoot, scriptPath, candidate)
|
|
389
|
+
if (resolved) {
|
|
390
|
+
return resolved
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const matches = scriptContent.matchAll(
|
|
395
|
+
/['"`]([^'"`\n]*rspack[^'"`\n]*config[^'"`\n]*\.(?:js|ts|mjs|cjs))['"`]/g,
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
for (const match of matches) {
|
|
399
|
+
const candidate = match[1]
|
|
400
|
+
if (!candidate) continue
|
|
401
|
+
const resolved = await resolveScriptRelativeCandidate(targetRoot, scriptPath, candidate)
|
|
402
|
+
if (resolved) {
|
|
403
|
+
return resolved
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return null
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
async function resolveWebpackBaseConfigFromFile(
|
|
411
|
+
targetRoot: string,
|
|
412
|
+
configPath: string,
|
|
413
|
+
): Promise<string | null> {
|
|
414
|
+
const configContent = await readFile(path.join(targetRoot, configPath))
|
|
415
|
+
if (!configContent) {
|
|
416
|
+
return null
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
for (const candidate of extractConfigArgs(configContent)) {
|
|
420
|
+
const resolved = await resolveScriptRelativeCandidate(targetRoot, configPath, candidate)
|
|
421
|
+
if (resolved) {
|
|
422
|
+
return resolved
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const matches = configContent.matchAll(
|
|
427
|
+
/(?:configPath\s*=\s*|require\()\s*['"`]([^'"`\n]*webpack[^'"`\n]*config[^'"`\n]*\.(?:js|ts|mjs|cjs))['"`]/g,
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
for (const match of matches) {
|
|
431
|
+
const candidate = match[1]
|
|
432
|
+
if (!candidate) continue
|
|
433
|
+
const resolved = await resolveScriptRelativeCandidate(targetRoot, configPath, candidate)
|
|
434
|
+
if (resolved) {
|
|
435
|
+
return resolved
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return null
|
|
440
|
+
}
|
|
441
|
+
|
|
283
442
|
async function detectPattern({
|
|
284
443
|
pattern,
|
|
285
444
|
workspaceRoot,
|
|
@@ -339,13 +498,47 @@ async function detectPattern({
|
|
|
339
498
|
pattern.tool === 'rspack' ||
|
|
340
499
|
pattern.tool === 'rsbuild')
|
|
341
500
|
) {
|
|
342
|
-
|
|
501
|
+
const rankedScripts = Object.entries(scripts).sort(
|
|
502
|
+
([leftName, leftCommand], [rightName, rightCommand]) =>
|
|
503
|
+
rankScriptCommand(rightName, rightCommand) - rankScriptCommand(leftName, leftCommand),
|
|
504
|
+
)
|
|
505
|
+
|
|
506
|
+
for (const [, cmd] of rankedScripts) {
|
|
507
|
+
if (pattern.tool === 'webpack' || pattern.tool === 'rspack') {
|
|
508
|
+
for (const configArg of extractConfigArgs(cmd)) {
|
|
509
|
+
const resolvedConfig = await resolveScriptRelativeCandidate(targetRoot, '', configArg)
|
|
510
|
+
if (resolvedConfig && (cmd.includes(pattern.tool) || cmd.includes(`${pattern.tool}-`))) {
|
|
511
|
+
if (pattern.tool === 'webpack') {
|
|
512
|
+
detectedFile =
|
|
513
|
+
(await resolveWebpackBaseConfigFromFile(targetRoot, resolvedConfig)) ??
|
|
514
|
+
resolvedConfig
|
|
515
|
+
} else {
|
|
516
|
+
detectedFile =
|
|
517
|
+
(await resolveRspackConfigFromScript(targetRoot, resolvedConfig)) ?? resolvedConfig
|
|
518
|
+
}
|
|
519
|
+
break
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if (detectedFile) {
|
|
524
|
+
break
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
343
528
|
if (cmd.includes('node ')) {
|
|
344
529
|
const match = cmd.match(/node\s+([^\s]+\.(js|mjs|cjs|ts))/)
|
|
345
530
|
if (match && match[1]) {
|
|
346
531
|
if (await exists(path.join(targetRoot, match[1]))) {
|
|
347
532
|
if (cmd.includes(pattern.tool) || match[1].includes(pattern.tool)) {
|
|
348
|
-
|
|
533
|
+
if (pattern.tool === 'rspack') {
|
|
534
|
+
detectedFile =
|
|
535
|
+
(await resolveRspackConfigFromScript(targetRoot, match[1])) ?? match[1]
|
|
536
|
+
} else if (pattern.tool === 'webpack') {
|
|
537
|
+
detectedFile =
|
|
538
|
+
(await resolveWebpackBaseConfigFromFile(targetRoot, match[1])) ?? match[1]
|
|
539
|
+
} else {
|
|
540
|
+
detectedFile = match[1]
|
|
541
|
+
}
|
|
349
542
|
break
|
|
350
543
|
}
|
|
351
544
|
}
|
|
@@ -393,16 +586,11 @@ async function detectPattern({
|
|
|
393
586
|
let isLegacyWebpack = false
|
|
394
587
|
|
|
395
588
|
if (pattern.tool === 'rspack') {
|
|
396
|
-
|
|
397
|
-
if (
|
|
398
|
-
version &&
|
|
399
|
-
(version.includes('0.3.') || version.includes('0.2.') || version.includes('0.1.'))
|
|
400
|
-
) {
|
|
589
|
+
if (isLegacyRspackVersion(resolvedVersion)) {
|
|
401
590
|
isLegacyRspack = true
|
|
402
591
|
}
|
|
403
592
|
} else if (pattern.tool === 'webpack') {
|
|
404
|
-
|
|
405
|
-
if ((version && version.includes('^4')) || version?.startsWith('4.')) {
|
|
593
|
+
if (isLegacyWebpackVersion(resolvedVersion)) {
|
|
406
594
|
isLegacyWebpack = true
|
|
407
595
|
}
|
|
408
596
|
}
|