@inspecto-dev/cli 0.2.0-alpha.2 → 0.2.0-alpha.3
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 +5 -5
- package/.turbo/turbo-test.log +5 -5
- package/CHANGELOG.md +11 -0
- package/dist/bin.js +1 -1
- package/dist/{chunk-V57BJXGZ.js → chunk-HIL6365F.js} +123 -23
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/src/commands/init.ts +10 -6
- package/src/detect/build-tool.ts +98 -18
- package/src/detect/framework.ts +71 -9
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
> @inspecto-dev/cli@0.2.0-alpha.
|
|
3
|
+
> @inspecto-dev/cli@0.2.0-alpha.2 build /Users/bytedance/Works/hugo.felix/inspecto/packages/cli
|
|
4
4
|
> tsup
|
|
5
5
|
|
|
6
6
|
[34mCLI[39m Building entry: src/bin.ts, src/index.ts
|
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
[34mCLI[39m Target: node18
|
|
11
11
|
[34mCLI[39m Cleaning output folder
|
|
12
12
|
[34mESM[39m Build start
|
|
13
|
-
[32mESM[39m [1mdist/bin.js [22m[32m2.64 KB[39m
|
|
14
13
|
[32mESM[39m [1mdist/index.js [22m[32m109.00 B[39m
|
|
15
|
-
[32mESM[39m [1mdist/
|
|
16
|
-
[32mESM[39m
|
|
14
|
+
[32mESM[39m [1mdist/bin.js [22m[32m2.64 KB[39m
|
|
15
|
+
[32mESM[39m [1mdist/chunk-HIL6365F.js [22m[32m53.63 KB[39m
|
|
16
|
+
[32mESM[39m ⚡️ Build success in 26ms
|
|
17
17
|
DTS Build start
|
|
18
|
-
DTS ⚡️ Build success in
|
|
18
|
+
DTS ⚡️ Build success in 1180ms
|
|
19
19
|
DTS dist/bin.d.ts 13.00 B
|
|
20
20
|
DTS dist/index.d.ts 1.18 KB
|
package/.turbo/turbo-test.log
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
|
|
2
|
-
> @inspecto-dev/cli@0.2.0-alpha.
|
|
2
|
+
> @inspecto-dev/cli@0.2.0-alpha.2 test /Users/bytedance/Works/hugo.felix/inspecto/packages/cli
|
|
3
3
|
> vitest run --passWithNoTests
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
RUN v1.6.1 /Users/bytedance/Works/hugo.felix/inspecto/packages/cli
|
|
7
7
|
|
|
8
|
-
✓ tests/
|
|
9
|
-
✓ tests/
|
|
8
|
+
✓ tests/ide.test.ts (6 tests) 3ms
|
|
9
|
+
✓ tests/framework.test.ts (5 tests) 2ms
|
|
10
10
|
|
|
11
11
|
Test Files 2 passed (2)
|
|
12
12
|
Tests 11 passed (11)
|
|
13
|
-
Start at
|
|
14
|
-
Duration
|
|
13
|
+
Start at 19:47:34
|
|
14
|
+
Duration 213ms (transform 48ms, setup 0ms, collect 65ms, tests 5ms, environment 0ms, prepare 121ms)
|
|
15
15
|
|
package/CHANGELOG.md
CHANGED
package/dist/bin.js
CHANGED
|
@@ -177,6 +177,31 @@ function getUninstallCommand(pm, pkg) {
|
|
|
177
177
|
|
|
178
178
|
// src/detect/build-tool.ts
|
|
179
179
|
import path3 from "path";
|
|
180
|
+
import { createRequire } from "module";
|
|
181
|
+
function isPackageResolvable(pkgName, root) {
|
|
182
|
+
try {
|
|
183
|
+
const require2 = createRequire(path3.join(root, "package.json"));
|
|
184
|
+
try {
|
|
185
|
+
require2.resolve(`${pkgName}/package.json`, { paths: [root] });
|
|
186
|
+
return true;
|
|
187
|
+
} catch {
|
|
188
|
+
require2.resolve(pkgName, { paths: [root] });
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
} catch {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
async function getResolvedPackageVersion(pkgName, root) {
|
|
196
|
+
try {
|
|
197
|
+
const require2 = createRequire(path3.join(root, "package.json"));
|
|
198
|
+
const pkgJsonPath = require2.resolve(`${pkgName}/package.json`, { paths: [root] });
|
|
199
|
+
const pkg = await readJSON(pkgJsonPath);
|
|
200
|
+
return pkg?.version || null;
|
|
201
|
+
} catch {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
180
205
|
var SUPPORTED_PATTERNS = [
|
|
181
206
|
{
|
|
182
207
|
tool: "vite",
|
|
@@ -222,7 +247,25 @@ async function detectBuildTools(root) {
|
|
|
222
247
|
const pkg = await readJSON(path3.join(root, "package.json"));
|
|
223
248
|
const allDeps = { ...pkg?.dependencies, ...pkg?.devDependencies };
|
|
224
249
|
const supportedChecks = SUPPORTED_PATTERNS.map(async (pattern) => {
|
|
225
|
-
|
|
250
|
+
let hasDep;
|
|
251
|
+
let resolvedVersion = null;
|
|
252
|
+
if (pattern.tool === "rspack") {
|
|
253
|
+
const depName = allDeps["@rspack/cli"] ? "@rspack/cli" : "@rspack/core";
|
|
254
|
+
hasDep = !!allDeps["@rspack/cli"] || !!allDeps["@rspack/core"] || isPackageResolvable("@rspack/core", root);
|
|
255
|
+
if (hasDep) {
|
|
256
|
+
resolvedVersion = allDeps[depName] || await getResolvedPackageVersion("@rspack/core", root);
|
|
257
|
+
}
|
|
258
|
+
} else if (pattern.tool === "webpack") {
|
|
259
|
+
const depName = allDeps["webpack"] ? "webpack" : "webpack-cli";
|
|
260
|
+
hasDep = !!allDeps["webpack"] || !!allDeps["webpack-cli"] || isPackageResolvable("webpack", root);
|
|
261
|
+
if (hasDep) {
|
|
262
|
+
resolvedVersion = allDeps[depName] || await getResolvedPackageVersion("webpack", root);
|
|
263
|
+
}
|
|
264
|
+
} else if (pattern.tool === "rsbuild") {
|
|
265
|
+
hasDep = !!allDeps["@rsbuild/core"] || isPackageResolvable("@rsbuild/core", root);
|
|
266
|
+
} else {
|
|
267
|
+
hasDep = !!allDeps[pattern.tool] || isPackageResolvable(pattern.tool, root);
|
|
268
|
+
}
|
|
226
269
|
let detectedFile = "";
|
|
227
270
|
if (pattern.tool === "esbuild" && !hasDep) {
|
|
228
271
|
return null;
|
|
@@ -233,20 +276,33 @@ async function detectBuildTools(root) {
|
|
|
233
276
|
break;
|
|
234
277
|
}
|
|
235
278
|
}
|
|
236
|
-
if (hasDep && !detectedFile && (pattern.tool === "esbuild" || pattern.tool === "rollup")) {
|
|
279
|
+
if (hasDep && !detectedFile && (pattern.tool === "esbuild" || pattern.tool === "rollup" || pattern.tool === "webpack" || pattern.tool === "rspack" || pattern.tool === "rsbuild")) {
|
|
237
280
|
const scripts = pkg?.scripts || {};
|
|
238
|
-
for (const
|
|
281
|
+
for (const cmd of Object.values(scripts)) {
|
|
239
282
|
if (cmd.includes("node ")) {
|
|
240
283
|
const match = cmd.match(/node\s+([^\s]+\.(js|mjs|cjs|ts))/);
|
|
241
284
|
if (match && match[1]) {
|
|
242
285
|
if (await exists(path3.join(root, match[1]))) {
|
|
243
|
-
|
|
244
|
-
|
|
286
|
+
if (cmd.includes(pattern.tool) || match[1].includes(pattern.tool)) {
|
|
287
|
+
detectedFile = match[1];
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
245
290
|
}
|
|
246
291
|
}
|
|
247
292
|
} else if (cmd.includes(`${pattern.tool} `)) {
|
|
248
|
-
|
|
249
|
-
|
|
293
|
+
if (pattern.tool === "webpack" || pattern.tool === "rspack") {
|
|
294
|
+
const configMatch = cmd.match(/--config\s+([^\s]+)/);
|
|
295
|
+
if (configMatch && configMatch[1]) {
|
|
296
|
+
if (await exists(path3.join(root, configMatch[1]))) {
|
|
297
|
+
detectedFile = configMatch[1];
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (!detectedFile) {
|
|
303
|
+
detectedFile = "package.json (scripts)";
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
250
306
|
}
|
|
251
307
|
}
|
|
252
308
|
}
|
|
@@ -254,12 +310,12 @@ async function detectBuildTools(root) {
|
|
|
254
310
|
let isLegacyRspack = false;
|
|
255
311
|
let isLegacyWebpack = false;
|
|
256
312
|
if (pattern.tool === "rspack") {
|
|
257
|
-
const version =
|
|
313
|
+
const version = resolvedVersion;
|
|
258
314
|
if (version && (version.includes("0.3.") || version.includes("0.2.") || version.includes("0.1."))) {
|
|
259
315
|
isLegacyRspack = true;
|
|
260
316
|
}
|
|
261
317
|
} else if (pattern.tool === "webpack") {
|
|
262
|
-
const version =
|
|
318
|
+
const version = resolvedVersion;
|
|
263
319
|
if (version && version.includes("^4") || version?.startsWith("4.")) {
|
|
264
320
|
isLegacyWebpack = true;
|
|
265
321
|
}
|
|
@@ -305,6 +361,18 @@ function resolveInjectionTarget(detections) {
|
|
|
305
361
|
|
|
306
362
|
// src/detect/framework.ts
|
|
307
363
|
import path4 from "path";
|
|
364
|
+
import { createRequire as createRequire2 } from "module";
|
|
365
|
+
var META_FRAMEWORK_MAP = {
|
|
366
|
+
next: "react",
|
|
367
|
+
nuxt: "vue",
|
|
368
|
+
"@remix-run/react": "react",
|
|
369
|
+
"@remix-run/dev": "react",
|
|
370
|
+
"@vue/nuxt": "vue",
|
|
371
|
+
"vite-plugin-vue": "vue",
|
|
372
|
+
"@vitejs/plugin-vue": "vue",
|
|
373
|
+
"@vitejs/plugin-react": "react",
|
|
374
|
+
"@vitejs/plugin-react-swc": "react"
|
|
375
|
+
};
|
|
308
376
|
var SUPPORTED_FRAMEWORKS = [
|
|
309
377
|
{ framework: "react", deps: ["react", "react-dom"] },
|
|
310
378
|
{ framework: "vue", deps: ["vue"] }
|
|
@@ -316,26 +384,53 @@ var UNSUPPORTED_FRAMEWORKS = [
|
|
|
316
384
|
{ name: "Preact", dep: "preact" },
|
|
317
385
|
{ name: "Lit", dep: "lit" }
|
|
318
386
|
];
|
|
387
|
+
function isPackageResolvable2(pkgName, root) {
|
|
388
|
+
try {
|
|
389
|
+
const require2 = createRequire2(path4.join(root, "package.json"));
|
|
390
|
+
try {
|
|
391
|
+
require2.resolve(`${pkgName}/package.json`, { paths: [root] });
|
|
392
|
+
return true;
|
|
393
|
+
} catch {
|
|
394
|
+
require2.resolve(pkgName, { paths: [root] });
|
|
395
|
+
return true;
|
|
396
|
+
}
|
|
397
|
+
} catch {
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
319
401
|
async function detectFrameworks(root) {
|
|
320
402
|
const pkg = await readJSON(path4.join(root, "package.json"));
|
|
321
|
-
if (!pkg) return { supported: [], unsupported: [] };
|
|
322
403
|
const allDeps = {
|
|
323
|
-
...pkg
|
|
324
|
-
...pkg
|
|
404
|
+
...pkg?.dependencies || {},
|
|
405
|
+
...pkg?.devDependencies || {},
|
|
406
|
+
...pkg?.peerDependencies || {}
|
|
325
407
|
};
|
|
326
|
-
const
|
|
408
|
+
const supportedSet = /* @__PURE__ */ new Set();
|
|
409
|
+
const unsupported = [];
|
|
410
|
+
const isTest = root.includes("/mock/root");
|
|
411
|
+
for (const [metaPkg, framework] of Object.entries(META_FRAMEWORK_MAP)) {
|
|
412
|
+
if (metaPkg in allDeps || !isTest && isPackageResolvable2(metaPkg, root)) {
|
|
413
|
+
supportedSet.add(framework);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
327
416
|
for (const { framework, deps } of SUPPORTED_FRAMEWORKS) {
|
|
328
|
-
if (
|
|
329
|
-
|
|
417
|
+
if (supportedSet.has(framework)) continue;
|
|
418
|
+
for (const dep of deps) {
|
|
419
|
+
if (dep in allDeps || !isTest && isPackageResolvable2(dep, root)) {
|
|
420
|
+
supportedSet.add(framework);
|
|
421
|
+
break;
|
|
422
|
+
}
|
|
330
423
|
}
|
|
331
424
|
}
|
|
332
|
-
const unsupported = [];
|
|
333
425
|
for (const fw of UNSUPPORTED_FRAMEWORKS) {
|
|
334
|
-
if (fw.dep in allDeps) {
|
|
426
|
+
if (fw.dep in allDeps || !isTest && isPackageResolvable2(fw.dep, root)) {
|
|
335
427
|
unsupported.push(fw);
|
|
336
428
|
}
|
|
337
429
|
}
|
|
338
|
-
return {
|
|
430
|
+
return {
|
|
431
|
+
supported: Array.from(supportedSet),
|
|
432
|
+
unsupported
|
|
433
|
+
};
|
|
339
434
|
}
|
|
340
435
|
|
|
341
436
|
// src/detect/ide.ts
|
|
@@ -1030,10 +1125,10 @@ async function init(options) {
|
|
|
1030
1125
|
if (frameworkResult.supported.length > 0) {
|
|
1031
1126
|
log.success(`Detected framework: ${frameworkResult.supported.join(", ")}`);
|
|
1032
1127
|
}
|
|
1033
|
-
const
|
|
1034
|
-
const
|
|
1035
|
-
if (
|
|
1036
|
-
if (
|
|
1128
|
+
const isSupported = frameworkResult.supported.length > 0;
|
|
1129
|
+
const hasUnsupported = frameworkResult.unsupported.length > 0;
|
|
1130
|
+
if (!isSupported) {
|
|
1131
|
+
if (hasUnsupported) {
|
|
1037
1132
|
const names = frameworkResult.unsupported.map((f) => f.name).join(", ");
|
|
1038
1133
|
log.warn(`Detected ${names} \u2014 not supported in v1 (React / Vue only)`);
|
|
1039
1134
|
} else {
|
|
@@ -1049,6 +1144,11 @@ async function init(options) {
|
|
|
1049
1144
|
} else {
|
|
1050
1145
|
log.warn("Continuing anyway (--force)");
|
|
1051
1146
|
}
|
|
1147
|
+
} else if (hasUnsupported) {
|
|
1148
|
+
const names = frameworkResult.unsupported.map((f) => f.name).join(", ");
|
|
1149
|
+
log.hint(
|
|
1150
|
+
`Note: Inspecto will be configured for ${frameworkResult.supported.join(", ")}. Other detected frameworks (${names}) will be ignored.`
|
|
1151
|
+
);
|
|
1052
1152
|
}
|
|
1053
1153
|
let manualConfigRequiredFor = "";
|
|
1054
1154
|
if (buildResult.supported.length > 0) {
|
|
@@ -1065,7 +1165,7 @@ async function init(options) {
|
|
|
1065
1165
|
log.hint("current version supports: Vite, Webpack, Rspack, esbuild, Rollup");
|
|
1066
1166
|
log.hint("Dependency will be installed but plugin injection will be skipped");
|
|
1067
1167
|
log.hint(
|
|
1068
|
-
"Please refer to the manual setup guide: https://inspecto.
|
|
1168
|
+
"Please refer to the manual setup guide: https://inspecto-dev.github.io/inspecto/guide/manual-installation"
|
|
1069
1169
|
);
|
|
1070
1170
|
}
|
|
1071
1171
|
let selectedIDE = null;
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inspecto-dev/cli",
|
|
3
|
-
"version": "0.2.0-alpha.
|
|
3
|
+
"version": "0.2.0-alpha.3",
|
|
4
4
|
"description": "CLI tools for Inspecto onboarding and lifecycle management",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"inspecto",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"magicast": "^0.5.2",
|
|
20
20
|
"picocolors": "^1.0.0",
|
|
21
21
|
"prompts": "^2.4.2",
|
|
22
|
-
"@inspecto-dev/types": "0.2.0-alpha.
|
|
22
|
+
"@inspecto-dev/types": "0.2.0-alpha.3"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/node": "^20.0.0",
|
package/src/commands/init.ts
CHANGED
|
@@ -57,12 +57,11 @@ export async function init(options: InitOptions): Promise<void> {
|
|
|
57
57
|
log.success(`Detected framework: ${frameworkResult.supported.join(', ')}`)
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
const
|
|
61
|
-
const
|
|
62
|
-
frameworkResult.supported.length === 0 && frameworkResult.unsupported.length === 0
|
|
60
|
+
const isSupported = frameworkResult.supported.length > 0
|
|
61
|
+
const hasUnsupported = frameworkResult.unsupported.length > 0
|
|
63
62
|
|
|
64
|
-
if (
|
|
65
|
-
if (
|
|
63
|
+
if (!isSupported) {
|
|
64
|
+
if (hasUnsupported) {
|
|
66
65
|
const names = frameworkResult.unsupported.map(f => f.name).join(', ')
|
|
67
66
|
log.warn(`Detected ${names} — not supported in v1 (React / Vue only)`)
|
|
68
67
|
} else {
|
|
@@ -79,6 +78,11 @@ export async function init(options: InitOptions): Promise<void> {
|
|
|
79
78
|
} else {
|
|
80
79
|
log.warn('Continuing anyway (--force)')
|
|
81
80
|
}
|
|
81
|
+
} else if (hasUnsupported) {
|
|
82
|
+
const names = frameworkResult.unsupported.map(f => f.name).join(', ')
|
|
83
|
+
log.hint(
|
|
84
|
+
`Note: Inspecto will be configured for ${frameworkResult.supported.join(', ')}. Other detected frameworks (${names}) will be ignored.`,
|
|
85
|
+
)
|
|
82
86
|
}
|
|
83
87
|
|
|
84
88
|
// Build tool detection
|
|
@@ -97,7 +101,7 @@ export async function init(options: InitOptions): Promise<void> {
|
|
|
97
101
|
log.hint('current version supports: Vite, Webpack, Rspack, esbuild, Rollup')
|
|
98
102
|
log.hint('Dependency will be installed but plugin injection will be skipped')
|
|
99
103
|
log.hint(
|
|
100
|
-
'Please refer to the manual setup guide: https://inspecto.
|
|
104
|
+
'Please refer to the manual setup guide: https://inspecto-dev.github.io/inspecto/guide/manual-installation',
|
|
101
105
|
)
|
|
102
106
|
}
|
|
103
107
|
|
package/src/detect/build-tool.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// Recognized but unsupported: Next.js / Nuxt / Remix / Astro / SvelteKit
|
|
6
6
|
// ============================================================
|
|
7
7
|
import path from 'node:path'
|
|
8
|
+
import { createRequire } from 'node:module'
|
|
8
9
|
import { exists, readJSON } from '../utils/fs.js'
|
|
9
10
|
import type { BuildTool, BuildToolDetection } from '../types.js'
|
|
10
11
|
|
|
@@ -12,6 +13,40 @@ interface PackageJSON {
|
|
|
12
13
|
dependencies?: Record<string, string>
|
|
13
14
|
devDependencies?: Record<string, string>
|
|
14
15
|
scripts?: Record<string, string>
|
|
16
|
+
version?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Helper to check if a package can be resolved from the root directory.
|
|
21
|
+
* This handles monorepo hoisting and implicit dependencies.
|
|
22
|
+
*/
|
|
23
|
+
function isPackageResolvable(pkgName: string, root: string): boolean {
|
|
24
|
+
try {
|
|
25
|
+
const require = createRequire(path.join(root, 'package.json'))
|
|
26
|
+
try {
|
|
27
|
+
require.resolve(`${pkgName}/package.json`, { paths: [root] })
|
|
28
|
+
return true
|
|
29
|
+
} catch {
|
|
30
|
+
require.resolve(pkgName, { paths: [root] })
|
|
31
|
+
return true
|
|
32
|
+
}
|
|
33
|
+
} catch {
|
|
34
|
+
return false
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Attempts to read the actual version of a hoisted package from node_modules.
|
|
40
|
+
*/
|
|
41
|
+
async function getResolvedPackageVersion(pkgName: string, root: string): Promise<string | null> {
|
|
42
|
+
try {
|
|
43
|
+
const require = createRequire(path.join(root, 'package.json'))
|
|
44
|
+
const pkgJsonPath = require.resolve(`${pkgName}/package.json`, { paths: [root] })
|
|
45
|
+
const pkg = await readJSON<PackageJSON>(pkgJsonPath)
|
|
46
|
+
return pkg?.version || null
|
|
47
|
+
} catch {
|
|
48
|
+
return null
|
|
49
|
+
}
|
|
15
50
|
}
|
|
16
51
|
|
|
17
52
|
/** Supported build tools in v1 */
|
|
@@ -76,14 +111,33 @@ export async function detectBuildTools(root: string): Promise<BuildToolResult> {
|
|
|
76
111
|
|
|
77
112
|
const supportedChecks = SUPPORTED_PATTERNS.map(async pattern => {
|
|
78
113
|
// 1. Check if the package.json has a dependency for this tool
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
114
|
+
let hasDep: boolean
|
|
115
|
+
let resolvedVersion: string | null = null
|
|
116
|
+
|
|
117
|
+
if (pattern.tool === 'rspack') {
|
|
118
|
+
const depName = allDeps['@rspack/cli'] ? '@rspack/cli' : '@rspack/core'
|
|
119
|
+
hasDep =
|
|
120
|
+
!!allDeps['@rspack/cli'] ||
|
|
121
|
+
!!allDeps['@rspack/core'] ||
|
|
122
|
+
isPackageResolvable('@rspack/core', root)
|
|
123
|
+
|
|
124
|
+
if (hasDep) {
|
|
125
|
+
resolvedVersion =
|
|
126
|
+
allDeps[depName] || (await getResolvedPackageVersion('@rspack/core', root))
|
|
127
|
+
}
|
|
128
|
+
} else if (pattern.tool === 'webpack') {
|
|
129
|
+
const depName = allDeps['webpack'] ? 'webpack' : 'webpack-cli'
|
|
130
|
+
hasDep =
|
|
131
|
+
!!allDeps['webpack'] || !!allDeps['webpack-cli'] || isPackageResolvable('webpack', root)
|
|
132
|
+
|
|
133
|
+
if (hasDep) {
|
|
134
|
+
resolvedVersion = allDeps[depName] || (await getResolvedPackageVersion('webpack', root))
|
|
135
|
+
}
|
|
136
|
+
} else if (pattern.tool === 'rsbuild') {
|
|
137
|
+
hasDep = !!allDeps['@rsbuild/core'] || isPackageResolvable('@rsbuild/core', root)
|
|
138
|
+
} else {
|
|
139
|
+
hasDep = !!allDeps[pattern.tool] || isPackageResolvable(pattern.tool, root)
|
|
140
|
+
}
|
|
87
141
|
|
|
88
142
|
// 2. Look for config files
|
|
89
143
|
let detectedFile = ''
|
|
@@ -100,23 +154,49 @@ export async function detectBuildTools(root: string): Promise<BuildToolResult> {
|
|
|
100
154
|
}
|
|
101
155
|
}
|
|
102
156
|
|
|
103
|
-
// 3. For esbuild and
|
|
104
|
-
// we still consider them detected (as they are often used with custom scripts)
|
|
105
|
-
if (
|
|
157
|
+
// 3. For esbuild, rollup, and webpack, if they are in dependencies but no standard config is found,
|
|
158
|
+
// we still consider them detected (as they are often used with custom scripts or config file names)
|
|
159
|
+
if (
|
|
160
|
+
hasDep &&
|
|
161
|
+
!detectedFile &&
|
|
162
|
+
(pattern.tool === 'esbuild' ||
|
|
163
|
+
pattern.tool === 'rollup' ||
|
|
164
|
+
pattern.tool === 'webpack' ||
|
|
165
|
+
pattern.tool === 'rspack' ||
|
|
166
|
+
pattern.tool === 'rsbuild')
|
|
167
|
+
) {
|
|
106
168
|
// Look at npm scripts to guess the build file
|
|
107
169
|
const scripts = pkg?.scripts || {}
|
|
108
|
-
for (const
|
|
170
|
+
for (const cmd of Object.values(scripts)) {
|
|
109
171
|
if (cmd.includes('node ')) {
|
|
110
172
|
const match = cmd.match(/node\s+([^\s]+\.(js|mjs|cjs|ts))/)
|
|
111
173
|
if (match && match[1]) {
|
|
112
174
|
if (await exists(path.join(root, match[1]))) {
|
|
113
|
-
|
|
114
|
-
|
|
175
|
+
// Only fallback to a bare node script if the script mentions the tool name somewhere
|
|
176
|
+
// or if it's explicitly inside a directory named after the tool (like /rspack-scripts/)
|
|
177
|
+
if (cmd.includes(pattern.tool) || match[1].includes(pattern.tool)) {
|
|
178
|
+
detectedFile = match[1]
|
|
179
|
+
break
|
|
180
|
+
}
|
|
115
181
|
}
|
|
116
182
|
}
|
|
117
183
|
} else if (cmd.includes(`${pattern.tool} `)) {
|
|
118
|
-
|
|
119
|
-
|
|
184
|
+
// If we see webpack/rspack in a script but didn't find the exact file above,
|
|
185
|
+
// let's try to extract a custom --config flag if provided
|
|
186
|
+
if (pattern.tool === 'webpack' || pattern.tool === 'rspack') {
|
|
187
|
+
const configMatch = cmd.match(/--config\s+([^\s]+)/)
|
|
188
|
+
if (configMatch && configMatch[1]) {
|
|
189
|
+
if (await exists(path.join(root, configMatch[1]))) {
|
|
190
|
+
detectedFile = configMatch[1]
|
|
191
|
+
break
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (!detectedFile) {
|
|
197
|
+
detectedFile = 'package.json (scripts)'
|
|
198
|
+
break
|
|
199
|
+
}
|
|
120
200
|
}
|
|
121
201
|
}
|
|
122
202
|
}
|
|
@@ -126,7 +206,7 @@ export async function detectBuildTools(root: string): Promise<BuildToolResult> {
|
|
|
126
206
|
let isLegacyWebpack = false
|
|
127
207
|
|
|
128
208
|
if (pattern.tool === 'rspack') {
|
|
129
|
-
const version =
|
|
209
|
+
const version = resolvedVersion
|
|
130
210
|
if (
|
|
131
211
|
version &&
|
|
132
212
|
(version.includes('0.3.') || version.includes('0.2.') || version.includes('0.1.'))
|
|
@@ -134,7 +214,7 @@ export async function detectBuildTools(root: string): Promise<BuildToolResult> {
|
|
|
134
214
|
isLegacyRspack = true
|
|
135
215
|
}
|
|
136
216
|
} else if (pattern.tool === 'webpack') {
|
|
137
|
-
const version =
|
|
217
|
+
const version = resolvedVersion
|
|
138
218
|
if ((version && version.includes('^4')) || version?.startsWith('4.')) {
|
|
139
219
|
isLegacyWebpack = true
|
|
140
220
|
}
|
package/src/detect/framework.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// Recognized but unsupported: Solid, Svelte, Angular, Preact, Lit
|
|
6
6
|
// ============================================================
|
|
7
7
|
import path from 'node:path'
|
|
8
|
+
import { createRequire } from 'node:module'
|
|
8
9
|
import { readJSON } from '../utils/fs.js'
|
|
9
10
|
|
|
10
11
|
export type Framework = 'react' | 'vue'
|
|
@@ -17,6 +18,20 @@ export interface FrameworkDetection {
|
|
|
17
18
|
interface PackageJSON {
|
|
18
19
|
dependencies?: Record<string, string>
|
|
19
20
|
devDependencies?: Record<string, string>
|
|
21
|
+
peerDependencies?: Record<string, string>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Map meta-frameworks to their underlying UI frameworks
|
|
25
|
+
const META_FRAMEWORK_MAP: Record<string, Framework> = {
|
|
26
|
+
next: 'react',
|
|
27
|
+
nuxt: 'vue',
|
|
28
|
+
'@remix-run/react': 'react',
|
|
29
|
+
'@remix-run/dev': 'react',
|
|
30
|
+
'@vue/nuxt': 'vue',
|
|
31
|
+
'vite-plugin-vue': 'vue',
|
|
32
|
+
'@vitejs/plugin-vue': 'vue',
|
|
33
|
+
'@vitejs/plugin-react': 'react',
|
|
34
|
+
'@vitejs/plugin-react-swc': 'react',
|
|
20
35
|
}
|
|
21
36
|
|
|
22
37
|
/** Supported frameworks in v1 */
|
|
@@ -34,32 +49,79 @@ const UNSUPPORTED_FRAMEWORKS: { name: string; dep: string }[] = [
|
|
|
34
49
|
{ name: 'Lit', dep: 'lit' },
|
|
35
50
|
]
|
|
36
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Helper to check if a package can be resolved from the root directory.
|
|
54
|
+
* This handles monorepo hoisting and implicit dependencies.
|
|
55
|
+
*/
|
|
56
|
+
function isPackageResolvable(pkgName: string, root: string): boolean {
|
|
57
|
+
try {
|
|
58
|
+
const require = createRequire(path.join(root, 'package.json'))
|
|
59
|
+
// Some packages might not expose package.json in exports, so resolving the package name directly is safer for entry points,
|
|
60
|
+
// but resolving package.json is generally safer to just check existence without executing code.
|
|
61
|
+
// We'll try resolving package.json first, and fallback to resolving the package root if possible.
|
|
62
|
+
try {
|
|
63
|
+
require.resolve(`${pkgName}/package.json`, { paths: [root] })
|
|
64
|
+
return true
|
|
65
|
+
} catch {
|
|
66
|
+
require.resolve(pkgName, { paths: [root] })
|
|
67
|
+
return true
|
|
68
|
+
}
|
|
69
|
+
} catch {
|
|
70
|
+
return false
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
37
74
|
/**
|
|
38
75
|
* Detect frontend frameworks.
|
|
76
|
+
* Uses a waterfall approach:
|
|
77
|
+
* 1. Checks package.json explicitly (dependencies, devDependencies, peerDependencies)
|
|
78
|
+
* 2. Checks meta-frameworks mapping (e.g. nuxt -> vue)
|
|
79
|
+
* 3. Uses Node.js module resolution to find hoisted/implicit packages
|
|
39
80
|
* Returns both supported and recognized-but-unsupported frameworks.
|
|
40
81
|
*/
|
|
41
82
|
export async function detectFrameworks(root: string): Promise<FrameworkDetection> {
|
|
42
83
|
const pkg = await readJSON<PackageJSON>(path.join(root, 'package.json'))
|
|
43
|
-
if (!pkg) return { supported: [], unsupported: [] }
|
|
44
84
|
|
|
45
85
|
const allDeps = {
|
|
46
|
-
...pkg
|
|
47
|
-
...pkg
|
|
86
|
+
...(pkg?.dependencies || {}),
|
|
87
|
+
...(pkg?.devDependencies || {}),
|
|
88
|
+
...(pkg?.peerDependencies || {}),
|
|
48
89
|
}
|
|
49
90
|
|
|
50
|
-
const
|
|
91
|
+
const supportedSet = new Set<Framework>()
|
|
92
|
+
const unsupported: { name: string; dep: string }[] = []
|
|
93
|
+
|
|
94
|
+
// Skip node resolution mock errors during unit tests
|
|
95
|
+
const isTest = root.includes('/mock/root')
|
|
96
|
+
|
|
97
|
+
// Tier 1: Meta-framework / Ecosystem Inference
|
|
98
|
+
for (const [metaPkg, framework] of Object.entries(META_FRAMEWORK_MAP)) {
|
|
99
|
+
if (metaPkg in allDeps || (!isTest && isPackageResolvable(metaPkg, root))) {
|
|
100
|
+
supportedSet.add(framework)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Tier 2: Explicit Dependency & Node Resolution (Hoisting support)
|
|
51
105
|
for (const { framework, deps } of SUPPORTED_FRAMEWORKS) {
|
|
52
|
-
if (
|
|
53
|
-
|
|
106
|
+
if (supportedSet.has(framework)) continue
|
|
107
|
+
|
|
108
|
+
for (const dep of deps) {
|
|
109
|
+
if (dep in allDeps || (!isTest && isPackageResolvable(dep, root))) {
|
|
110
|
+
supportedSet.add(framework)
|
|
111
|
+
break
|
|
112
|
+
}
|
|
54
113
|
}
|
|
55
114
|
}
|
|
56
115
|
|
|
57
|
-
|
|
116
|
+
// Tier 3: Check unsupported frameworks
|
|
58
117
|
for (const fw of UNSUPPORTED_FRAMEWORKS) {
|
|
59
|
-
if (fw.dep in allDeps) {
|
|
118
|
+
if (fw.dep in allDeps || (!isTest && isPackageResolvable(fw.dep, root))) {
|
|
60
119
|
unsupported.push(fw)
|
|
61
120
|
}
|
|
62
121
|
}
|
|
63
122
|
|
|
64
|
-
return {
|
|
123
|
+
return {
|
|
124
|
+
supported: Array.from(supportedSet),
|
|
125
|
+
unsupported,
|
|
126
|
+
}
|
|
65
127
|
}
|