mobile-debug-mcp 0.24.6 → 0.24.7
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/.github/workflows/ci.yml +1 -3
- package/README.md +7 -0
- package/dist/manage/android.js +9 -5
- package/dist/manage/index.js +37 -23
- package/dist/manage/ios.js +12 -15
- package/dist/server/common.js +46 -0
- package/dist/server/tool-handlers.js +120 -33
- package/dist/server-core.js +1 -1
- package/dist/utils/android/utils.js +17 -5
- package/dist/utils/cli/idb/check-idb.js +1 -1
- package/docs/CHANGELOG.md +15 -10
- package/eslint.config.js +2 -47
- package/package.json +7 -6
- package/src/manage/android.ts +22 -11
- package/src/manage/index.ts +37 -16
- package/src/manage/ios.ts +28 -15
- package/src/server/common.ts +50 -0
- package/src/server/tool-handlers.ts +136 -32
- package/src/server-core.ts +1 -1
- package/src/utils/android/utils.ts +18 -7
- package/src/utils/cli/idb/check-idb.ts +1 -1
- package/test/device/automated/observe/capture_screenshot.android.smoke.ts +1 -1
- package/test/device/automated/observe/capture_screenshot.ios.smoke.ts +1 -1
- package/test/device/automated/observe/get_logs.android.smoke.ts +1 -1
- package/test/device/automated/observe/get_logs.ios.smoke.ts +1 -1
- package/test/device/automated/observe/get_ui_tree.android.smoke.ts +1 -1
- package/test/device/automated/observe/get_ui_tree.ios.smoke.ts +1 -1
- package/test/device/manual/interact/app_lifecycle.manual.ts +3 -3
- package/test/device/manual/observe/capture_screenshot.manual.ts +2 -2
- package/test/device/manual/observe/get_logs.manual.ts +2 -2
- package/test/device/manual/observe/get_ui_tree.manual.ts +2 -2
- package/test/device/manual/observe/logstream.manual.ts +1 -1
- package/test/device/manual/observe/screen_fingerprint.manual.ts +2 -2
- package/test/unit/manage/scoped_env.test.ts +137 -0
- package/test/unit/server/capture_screenshot.test.ts +17 -0
- package/test/unit/server/common.test.ts +18 -0
- package/test/unit/server/contract.test.ts +3 -0
- package/test/unit/server/get_logs.test.ts +17 -0
- package/test/unit/server/get_network_activity.test.ts +17 -0
- package/test/unit/server/get_ui_tree.test.ts +17 -0
- package/test/unit/server/response_shapes.test.ts +18 -0
- package/test/unit/server/start_log_stream.test.ts +37 -0
- package/.eslintignore +0 -5
- package/.eslintrc.cjs +0 -18
- package/eslint.config.cjs +0 -36
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import assert from 'assert'
|
|
2
|
+
import { ToolsManage } from '../../../src/manage/index.js'
|
|
3
|
+
import { AndroidManage } from '../../../src/manage/android.js'
|
|
4
|
+
import { iOSManage } from '../../../src/manage/ios.js'
|
|
5
|
+
|
|
6
|
+
async function run() {
|
|
7
|
+
const originalAndroidBuild = AndroidManage.prototype.build
|
|
8
|
+
const originalIOSBuild = iOSManage.prototype.build
|
|
9
|
+
const originalGradleWorkers = process.env.MCP_GRADLE_WORKERS
|
|
10
|
+
const originalGradleCache = process.env.MCP_GRADLE_CACHE
|
|
11
|
+
const originalForceCleanAndroid = process.env.MCP_FORCE_CLEAN_ANDROID
|
|
12
|
+
const originalDerivedData = process.env.MCP_DERIVED_DATA
|
|
13
|
+
const originalXcodeJobs = process.env.MCP_XCODE_JOBS
|
|
14
|
+
const originalForceCleanIOS = process.env.MCP_FORCE_CLEAN
|
|
15
|
+
const originalDestination = process.env.MCP_XCODE_DESTINATION_UDID
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
process.env.MCP_GRADLE_WORKERS = 'ambient-workers'
|
|
19
|
+
process.env.MCP_GRADLE_CACHE = 'ambient-cache'
|
|
20
|
+
process.env.MCP_FORCE_CLEAN_ANDROID = 'ambient-force-android'
|
|
21
|
+
process.env.MCP_DERIVED_DATA = '/tmp/ambient-derived'
|
|
22
|
+
process.env.MCP_XCODE_JOBS = 'ambient-xcode-jobs'
|
|
23
|
+
process.env.MCP_FORCE_CLEAN = 'ambient-force-ios'
|
|
24
|
+
process.env.MCP_XCODE_DESTINATION_UDID = 'ambient-destination'
|
|
25
|
+
|
|
26
|
+
let androidCalls = 0
|
|
27
|
+
AndroidManage.prototype.build = async function (_projectPath: string, options?: { variant?: string, env?: Record<string, string | undefined> }) {
|
|
28
|
+
androidCalls += 1
|
|
29
|
+
if (androidCalls === 1) {
|
|
30
|
+
assert.strictEqual(options?.variant, 'assembleDebug')
|
|
31
|
+
assert.deepStrictEqual(options?.env, {
|
|
32
|
+
MCP_GRADLE_TASK: 'assembleDebug',
|
|
33
|
+
MCP_GRADLE_WORKERS: '3',
|
|
34
|
+
MCP_GRADLE_CACHE: '0',
|
|
35
|
+
MCP_FORCE_CLEAN_ANDROID: '1'
|
|
36
|
+
})
|
|
37
|
+
} else {
|
|
38
|
+
assert.strictEqual(options?.variant, 'assembleDebug')
|
|
39
|
+
assert.deepStrictEqual(options?.env, {
|
|
40
|
+
MCP_GRADLE_TASK: 'assembleDebug'
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
return { artifactPath: '/tmp/fake.apk' }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let iosCalls = 0
|
|
47
|
+
iOSManage.prototype.build = async function (_projectPath: string, options?: { variant?: string, workspace?: string, project?: string, scheme?: string, destinationUDID?: string, derivedDataPath?: string, buildJobs?: number, forceClean?: boolean, xcodeCmd?: string, env?: Record<string, string | undefined> }) {
|
|
48
|
+
iosCalls += 1
|
|
49
|
+
if (iosCalls === 1) {
|
|
50
|
+
assert.deepStrictEqual(options?.env, {
|
|
51
|
+
MCP_DERIVED_DATA: '/tmp/derived',
|
|
52
|
+
MCP_XCODE_JOBS: '4',
|
|
53
|
+
MCP_FORCE_CLEAN: '1',
|
|
54
|
+
MCP_XCODE_DESTINATION_UDID: 'booted'
|
|
55
|
+
})
|
|
56
|
+
} else if (iosCalls === 2) {
|
|
57
|
+
assert.deepStrictEqual(options?.env, {})
|
|
58
|
+
} else {
|
|
59
|
+
assert.deepStrictEqual(options?.env, {
|
|
60
|
+
MCP_FORCE_CLEAN: '0',
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
assert.strictEqual(options?.derivedDataPath, iosCalls === 1 ? '/tmp/derived' : undefined)
|
|
64
|
+
assert.strictEqual(options?.buildJobs, iosCalls === 1 ? 4 : undefined)
|
|
65
|
+
assert.strictEqual(options?.forceClean, iosCalls === 1 ? true : iosCalls === 3 ? false : undefined)
|
|
66
|
+
assert.strictEqual(options?.destinationUDID, iosCalls === 1 ? 'booted' : undefined)
|
|
67
|
+
return { artifactPath: '/tmp/Fake.app' }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
await ToolsManage.build_android({
|
|
71
|
+
projectPath: '/tmp/project',
|
|
72
|
+
maxWorkers: 3,
|
|
73
|
+
gradleCache: false,
|
|
74
|
+
forceClean: true
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
await ToolsManage.build_ios({
|
|
78
|
+
projectPath: '/tmp/project',
|
|
79
|
+
derivedDataPath: '/tmp/derived',
|
|
80
|
+
buildJobs: 4,
|
|
81
|
+
forceClean: true,
|
|
82
|
+
destinationUDID: 'booted'
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
await ToolsManage.build_android({
|
|
86
|
+
projectPath: '/tmp/project'
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
await ToolsManage.build_ios({
|
|
90
|
+
projectPath: '/tmp/project'
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
await ToolsManage.build_ios({
|
|
94
|
+
projectPath: '/tmp/project',
|
|
95
|
+
forceClean: false
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
assert.strictEqual(process.env.MCP_GRADLE_WORKERS, 'ambient-workers')
|
|
99
|
+
assert.strictEqual(process.env.MCP_GRADLE_CACHE, 'ambient-cache')
|
|
100
|
+
assert.strictEqual(process.env.MCP_FORCE_CLEAN_ANDROID, 'ambient-force-android')
|
|
101
|
+
assert.strictEqual(process.env.MCP_DERIVED_DATA, '/tmp/ambient-derived')
|
|
102
|
+
assert.strictEqual(process.env.MCP_XCODE_JOBS, 'ambient-xcode-jobs')
|
|
103
|
+
assert.strictEqual(process.env.MCP_FORCE_CLEAN, 'ambient-force-ios')
|
|
104
|
+
assert.strictEqual(process.env.MCP_XCODE_DESTINATION_UDID, 'ambient-destination')
|
|
105
|
+
|
|
106
|
+
console.log('manage scoped env tests passed')
|
|
107
|
+
} finally {
|
|
108
|
+
AndroidManage.prototype.build = originalAndroidBuild
|
|
109
|
+
iOSManage.prototype.build = originalIOSBuild
|
|
110
|
+
|
|
111
|
+
if (originalGradleWorkers === undefined) delete process.env.MCP_GRADLE_WORKERS
|
|
112
|
+
else process.env.MCP_GRADLE_WORKERS = originalGradleWorkers
|
|
113
|
+
|
|
114
|
+
if (originalGradleCache === undefined) delete process.env.MCP_GRADLE_CACHE
|
|
115
|
+
else process.env.MCP_GRADLE_CACHE = originalGradleCache
|
|
116
|
+
|
|
117
|
+
if (originalForceCleanAndroid === undefined) delete process.env.MCP_FORCE_CLEAN_ANDROID
|
|
118
|
+
else process.env.MCP_FORCE_CLEAN_ANDROID = originalForceCleanAndroid
|
|
119
|
+
|
|
120
|
+
if (originalDerivedData === undefined) delete process.env.MCP_DERIVED_DATA
|
|
121
|
+
else process.env.MCP_DERIVED_DATA = originalDerivedData
|
|
122
|
+
|
|
123
|
+
if (originalXcodeJobs === undefined) delete process.env.MCP_XCODE_JOBS
|
|
124
|
+
else process.env.MCP_XCODE_JOBS = originalXcodeJobs
|
|
125
|
+
|
|
126
|
+
if (originalForceCleanIOS === undefined) delete process.env.MCP_FORCE_CLEAN
|
|
127
|
+
else process.env.MCP_FORCE_CLEAN = originalForceCleanIOS
|
|
128
|
+
|
|
129
|
+
if (originalDestination === undefined) delete process.env.MCP_XCODE_DESTINATION_UDID
|
|
130
|
+
else process.env.MCP_XCODE_DESTINATION_UDID = originalDestination
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
run().catch((error) => {
|
|
135
|
+
console.error(error)
|
|
136
|
+
process.exit(1)
|
|
137
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import assert from 'assert'
|
|
2
|
+
import { handleToolCall } from '../../../src/server-core.js'
|
|
3
|
+
|
|
4
|
+
async function run() {
|
|
5
|
+
const result = await handleToolCall('capture_screenshot', {})
|
|
6
|
+
const payload = JSON.parse(result.content[0].text)
|
|
7
|
+
|
|
8
|
+
assert.strictEqual(payload.error.tool, 'capture_screenshot')
|
|
9
|
+
assert.match(payload.error.message, /Missing or invalid string argument: platform/)
|
|
10
|
+
|
|
11
|
+
console.log('capture_screenshot argument tests passed')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
run().catch((error) => {
|
|
15
|
+
console.error(error)
|
|
16
|
+
process.exit(1)
|
|
17
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import assert from 'assert'
|
|
2
|
+
import { requireBooleanArg } from '../../../src/server/common.js'
|
|
3
|
+
|
|
4
|
+
function run() {
|
|
5
|
+
assert.strictEqual(requireBooleanArg({ exact: true }, 'exact'), true)
|
|
6
|
+
assert.strictEqual(requireBooleanArg({ exact: false }, 'exact'), false)
|
|
7
|
+
assert.throws(() => requireBooleanArg({}, 'exact'), /Missing or invalid boolean argument: exact/)
|
|
8
|
+
assert.throws(() => requireBooleanArg({ exact: 'true' as unknown as boolean }, 'exact'), /Missing or invalid boolean argument: exact/)
|
|
9
|
+
|
|
10
|
+
console.log('server common tests passed')
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
run()
|
|
15
|
+
} catch (error) {
|
|
16
|
+
console.error(error)
|
|
17
|
+
process.exit(1)
|
|
18
|
+
}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import assert from 'assert'
|
|
2
|
+
import { readFileSync } from 'fs'
|
|
2
3
|
import { handleToolCall, serverInfo, toolDefinitions } from '../../../src/server-core.js'
|
|
3
4
|
|
|
4
5
|
async function run() {
|
|
6
|
+
const packageJson = JSON.parse(readFileSync(new URL('../../../package.json', import.meta.url), 'utf8'))
|
|
5
7
|
const names = toolDefinitions.map((tool) => tool.name)
|
|
6
8
|
const uniqueNames = new Set(names)
|
|
7
9
|
|
|
8
10
|
assert.strictEqual(serverInfo.name, 'mobile-debug-mcp')
|
|
11
|
+
assert.strictEqual(serverInfo.version, packageJson.version, 'serverInfo version should match package.json')
|
|
9
12
|
assert.strictEqual(names.length, uniqueNames.size, 'tool names should be unique')
|
|
10
13
|
assert(names.includes('wait_for_ui'))
|
|
11
14
|
assert(names.includes('expect_screen'))
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import assert from 'assert'
|
|
2
|
+
import { handleToolCall } from '../../../src/server-core.js'
|
|
3
|
+
|
|
4
|
+
async function run() {
|
|
5
|
+
const result = await handleToolCall('get_logs', {})
|
|
6
|
+
const payload = JSON.parse(result.content[0].text)
|
|
7
|
+
|
|
8
|
+
assert.strictEqual(payload.error.tool, 'get_logs')
|
|
9
|
+
assert.match(payload.error.message, /Missing or invalid string argument: platform/)
|
|
10
|
+
|
|
11
|
+
console.log('get_logs argument tests passed')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
run().catch((error) => {
|
|
15
|
+
console.error(error)
|
|
16
|
+
process.exit(1)
|
|
17
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import assert from 'assert'
|
|
2
|
+
import { handleToolCall } from '../../../src/server-core.js'
|
|
3
|
+
|
|
4
|
+
async function run() {
|
|
5
|
+
const result = await handleToolCall('get_network_activity', {})
|
|
6
|
+
const payload = JSON.parse(result.content[0].text)
|
|
7
|
+
|
|
8
|
+
assert.strictEqual(payload.error.tool, 'get_network_activity')
|
|
9
|
+
assert.match(payload.error.message, /Missing or invalid string argument: platform/)
|
|
10
|
+
|
|
11
|
+
console.log('get_network_activity argument tests passed')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
run().catch((error) => {
|
|
15
|
+
console.error(error)
|
|
16
|
+
process.exit(1)
|
|
17
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import assert from 'assert'
|
|
2
|
+
import { handleToolCall } from '../../../src/server-core.js'
|
|
3
|
+
|
|
4
|
+
async function run() {
|
|
5
|
+
const result = await handleToolCall('get_ui_tree', {})
|
|
6
|
+
const payload = JSON.parse(result.content[0].text)
|
|
7
|
+
|
|
8
|
+
assert.strictEqual(payload.error.tool, 'get_ui_tree')
|
|
9
|
+
assert.match(payload.error.message, /Missing or invalid string argument: platform/)
|
|
10
|
+
|
|
11
|
+
console.log('get_ui_tree argument tests passed')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
run().catch((error) => {
|
|
15
|
+
console.error(error)
|
|
16
|
+
process.exit(1)
|
|
17
|
+
})
|
|
@@ -32,6 +32,15 @@ async function run() {
|
|
|
32
32
|
assert.strictEqual(installPayload.output, 'Success')
|
|
33
33
|
assert.strictEqual(installPayload.device.id, 'emulator-5554')
|
|
34
34
|
|
|
35
|
+
const missingBuildResponse = await handleToolCall('build_app', { projectPath: '/tmp/project' })
|
|
36
|
+
const missingBuildPayload = JSON.parse((missingBuildResponse as any).content[0].text)
|
|
37
|
+
assert.deepStrictEqual(missingBuildPayload, {
|
|
38
|
+
error: {
|
|
39
|
+
tool: 'build_app',
|
|
40
|
+
message: 'Missing or invalid string argument: platform'
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
|
|
35
44
|
;(ToolsInteract as any).waitForUIHandler = async () => ({
|
|
36
45
|
status: 'success',
|
|
37
46
|
matched: 1,
|
|
@@ -156,6 +165,15 @@ async function run() {
|
|
|
156
165
|
assert.match(objectTapPayload.error.message, /"code": "E_CUSTOM"/)
|
|
157
166
|
assert.match(objectTapPayload.error.message, /"field": "value"/)
|
|
158
167
|
|
|
168
|
+
const missingArgResponse = await handleToolCall('tap', { platform: 'android', x: 1 })
|
|
169
|
+
const missingArgPayload = JSON.parse((missingArgResponse as any).content[0].text)
|
|
170
|
+
assert.deepStrictEqual(missingArgPayload, {
|
|
171
|
+
error: {
|
|
172
|
+
tool: 'tap',
|
|
173
|
+
message: 'Missing or invalid number argument: y'
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
|
|
159
177
|
;(ToolsObserve as any).captureScreenshotHandler = async () => ({
|
|
160
178
|
device: { platform: 'ios', id: 'booted', osVersion: '18.0', model: 'Simulator', simulator: true },
|
|
161
179
|
screenshot: Buffer.from('png-data').toString('base64'),
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import assert from 'assert'
|
|
2
|
+
import { handleToolCall } from '../../../src/server-core.js'
|
|
3
|
+
import { ToolsObserve } from '../../../src/observe/index.js'
|
|
4
|
+
|
|
5
|
+
async function run() {
|
|
6
|
+
const originalStartLogStreamHandler = (ToolsObserve as any).startLogStreamHandler
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
let captured: any = null
|
|
10
|
+
;(ToolsObserve as any).startLogStreamHandler = async (args: any) => {
|
|
11
|
+
captured = args
|
|
12
|
+
return { sessionId: 'session-1', started: true }
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const result = await handleToolCall('start_log_stream', { packageName: 'com.example.app' })
|
|
16
|
+
const payload = JSON.parse(result.content[0].text)
|
|
17
|
+
|
|
18
|
+
assert.deepStrictEqual(captured, {
|
|
19
|
+
platform: 'android',
|
|
20
|
+
packageName: 'com.example.app',
|
|
21
|
+
level: 'error',
|
|
22
|
+
sessionId: undefined,
|
|
23
|
+
deviceId: undefined
|
|
24
|
+
})
|
|
25
|
+
assert.strictEqual(payload.sessionId, 'session-1')
|
|
26
|
+
assert.strictEqual(payload.started, true)
|
|
27
|
+
|
|
28
|
+
console.log('start_log_stream default tests passed')
|
|
29
|
+
} finally {
|
|
30
|
+
;(ToolsObserve as any).startLogStreamHandler = originalStartLogStreamHandler
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
run().catch((error) => {
|
|
35
|
+
console.error(error)
|
|
36
|
+
process.exit(1)
|
|
37
|
+
})
|
package/.eslintignore
DELETED
package/.eslintrc.cjs
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
root: true,
|
|
3
|
-
parser: '@typescript-eslint/parser',
|
|
4
|
-
parserOptions: {
|
|
5
|
-
ecmaVersion: 2020,
|
|
6
|
-
sourceType: 'module',
|
|
7
|
-
project: './tsconfig.json'
|
|
8
|
-
},
|
|
9
|
-
plugins: ['@typescript-eslint', 'unused-imports'],
|
|
10
|
-
rules: {
|
|
11
|
-
// Use plugin to error on unused imports and provide autofix where possible
|
|
12
|
-
'unused-imports/no-unused-imports': 'error',
|
|
13
|
-
'unused-imports/no-unused-vars': ['error', { vars: 'all', args: 'after-used', ignoreRestSiblings: true }],
|
|
14
|
-
// Disable the default TS rule to avoid duplicate warnings
|
|
15
|
-
'@typescript-eslint/no-unused-vars': 'off'
|
|
16
|
-
},
|
|
17
|
-
ignorePatterns: ['dist/', 'node_modules/', '.git/']
|
|
18
|
-
}
|
package/eslint.config.cjs
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
module.exports = [
|
|
2
|
-
// Files/directories to ignore
|
|
3
|
-
{
|
|
4
|
-
ignores: [
|
|
5
|
-
'dist/',
|
|
6
|
-
'node_modules/',
|
|
7
|
-
'.git/',
|
|
8
|
-
'.vscode/',
|
|
9
|
-
'coverage/',
|
|
10
|
-
'.env'
|
|
11
|
-
]
|
|
12
|
-
},
|
|
13
|
-
// Apply rules to JS/TS source and tests
|
|
14
|
-
{
|
|
15
|
-
files: ['src/**/*.ts', 'test/**/*.ts', 'src/**/*.js', 'test/**/*.js'],
|
|
16
|
-
languageOptions: {
|
|
17
|
-
parser: require.resolve('@typescript-eslint/parser'),
|
|
18
|
-
parserOptions: {
|
|
19
|
-
ecmaVersion: 2020,
|
|
20
|
-
sourceType: 'module',
|
|
21
|
-
project: './tsconfig.json'
|
|
22
|
-
}
|
|
23
|
-
},
|
|
24
|
-
plugins: {
|
|
25
|
-
'@typescript-eslint': require('@typescript-eslint/eslint-plugin'),
|
|
26
|
-
'unused-imports': require('eslint-plugin-unused-imports')
|
|
27
|
-
},
|
|
28
|
-
rules: {
|
|
29
|
-
// Use plugin to error on unused imports and provide autofix where possible
|
|
30
|
-
'unused-imports/no-unused-imports': 'error',
|
|
31
|
-
'unused-imports/no-unused-vars': ['error', { vars: 'all', args: 'after-used', ignoreRestSiblings: true }],
|
|
32
|
-
// Disable the default TS rule to avoid duplicate warnings
|
|
33
|
-
'@typescript-eslint/no-unused-vars': 'off'
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
]
|